Ir ao conteúdo
  • Cadastre-se

Pesquisar na Comunidade

Mostrando resultados para as tags ''PIC''.



Mais opções de pesquisa

  • Pesquisar por Tags

    Digite tags separadas por vírgulas
  • Pesquisar por Autor

Tipo de Conteúdo


Categorias

  • Armazenamento
  • Áudio
  • Energia
  • Entrada
  • Feiras e Eventos
  • Gabinetes
  • Memória
  • Museu
  • Placas-mãe
  • Portáteis
  • Processadores
  • Programas
  • Rádio CdH
  • Redes
  • Refrigeração
  • Smartphones
  • Tablets
  • Vídeo
  • Outros

Categorias

  • Armazenamento
  • Áudio
  • Câmeras
  • Computadores
  • Consoles
  • Eletrônicos
  • Energia
  • Entrada
  • Gabinetes
  • Impressão
  • Jogos
  • Memória
  • Placas-mãe
  • Portáteis
  • Processadores
  • Programas
  • Redes
  • Refrigeração
  • Smartphones
  • Tablets
  • Vídeo
  • Outros

Categorias

  • Livros disponíveis
  • Livros esgotados

Fóruns

  • Mensagens do Clube do Hardware
    • Regras gerais
    • Boletins do Clube do Hardware
    • Notícias da administração
  • Hardware
    • Recomendações de computadores
    • Placas de vídeo
    • Placas-mãe e chipsets
    • Processadores
    • Problemas de gargalo e desempenho
    • Memórias
    • Armazenamento
    • Refrigeração e superaquecimento
    • Fontes e energia
    • Gabinetes e casemods
    • Placas de som e áudio on-board
    • Periféricos
    • Mac
    • Overclock
    • Hardware - outros
  • Computação móvel
    • Notebooks
    • Tablets
    • Smartphones e apps
  • Redes e Internet
    • Redes e Internet
    • Hospedagem de sites e registro de domínios
  • Segurança da informação
    • Remoção de malware
    • Dúvidas sobre invasões e infecções
    • Programas de proteção
  • Programação e desenvolvimento
    • Java
    • C/C#/C++
    • Pascal/Delphi
    • .NET
    • Bancos de dados
    • Programação web
    • Programação - iniciantes
    • Programação de microcontroladores
    • Programação - outros
  • Software
    • Jogos
    • Pacotes de escritório
    • Computação distribuída (Folding@Home, Bitcoin etc.)
    • Virtualização
    • Design, animação e tratamento de imagens
    • Edição, autoração e gravação de áudio e vídeo
    • Programas
  • Sistemas Operacionais
  • Eletrônicos
  • Outros
  • Clube do Hardware

Categorias

  • Hardware
  • Redes
  • Eletrônica
  • Sistemas operacionais

Encontrar resultados em...

Encontrar resultados que...


Data de criação

  • Iniciar

    FIM


Última atualização

  • Iniciar

    FIM


Filtrar pelo número de...

Data de registro

  • Iniciar

    FIM


Grupo


Encontrado 204 registros

  1. Olá, Este trecho de código: // Bibliotecas #include "config.h" #include <xc.h> //Definições #define _XTAL_FREQ 8000000 // cristal interno void delay_ms(unsigned int delayvar) { // Variáveis locais unsigned int i; unsigned long j; for(i=0;i<delayvar;i++){ // loop para amarrar em X ms - Onde X é passado através de "delayvar" for(j=0;j<_XTAL_FREQ/65500;j++);// loop de processamento vazio - consome 1ms em média } } Faz com que instrução "for(j=0;j<_XTAL_FREQ/65500;j++);" consuma 1ms. Se executar essa instrução 10 vezes terei um delay de 10ms. Faço isso chamando a função desta maneira "delay_ms(10)". Estou a interpretar bem o código? Estou com a versão Mplab X IDE V3.40. Existe maneira de monitorizar o tempo de execução de cada instrução? (sei que sim, mas nesta versão do MPLAB não sei como chegar ao ecrã que permite ver o tempo de execução por cada instrução. Valeu
  2. Boa Noite Pessoal, estou com um problema com a comunicação USB com pic18f2550. Construí um dispositivo usb(joystick), porém no proteus ele funciona normal, no MCU ele reconhece, mas os botões e eixos nao funcionam. codigo: unsigned char readbuff[64] absolute 0x500; unsigned char writebuff[64] absolute 0x540; char btn = 0x00; short eixoX, eixoY, eixoZ = 0; void interrupt() { USB_Interrupt_Proc(); } void readJoy(){ ADC_Init(); //------------ Bloco que converte o eixo Y -------------------- eixoY = ADC_Read(1)/4; eixoY = eixoY - 127; if(eixoY >= -5 && eixoY <= 5)PORTA.RA4 = 1; else PORTA.RA4 = 0; //----------- Conversão do eixo X ------------- eixoX = ADC_Read(0)/4; eixoX = eixoX - 127; if(eixoX >= -5 && eixoX <= 5)PORTA.RA3 = 1; else PORTA.RA3 = 0; //----------- Conversão do eixo Z digital----------- eixoZ = 0; if(!PORTA.RA2) eixoZ = 127; else {eixoZ = -127;} //------------ Acionamento dos botões ----------------- if(!PORTB.RB0) btn.RB0 = 1; else btn.RB0 = 0; if(!PORTB.RB1) btn.RB1 = 1; else btn.RB1 = 0; if(!PORTB.RB2) btn.RB2 = 1; else btn.RB2 = 0; if(!PORTB.RB3) btn.RB3 = 1; else btn.RB3 = 0; if(!PORTB.RB4) btn.RB4 = 1; else btn.RB4 = 0; if(!PORTB.RB5) btn.RB5 = 1; else btn.RB5 = 0; if(!PORTB.RB6) btn.RB6 = 1; else btn.RB6 = 0; if(!PORTB.RB7) btn.RB7 = 1; else btn.RB7 = 0; // -------------- envia os dados para o buffer do usb ------------ writebuff[0] = btn; writebuff[1] = eixoX; writebuff[2] = ~eixoY; writebuff[3] = eixoZ; while(!HID_Write(writebuff,9)); } void main() { TRISB = 255;//define portb como entrada TRISA = 255; TRISC = 255; ADCON1 = 0x0D;//altera as entradas A0 e A1 analogicas PORTA = 0; PORTB = 0; PORTC = 0; CMCON = 0x07; HID_Enable(&readbuff, &writebuff); while(1){ readJoy(); Delay_ms(100); } } HID Descritor: const unsigned int USB_VENDOR_ID = 0x0123; const unsigned int USB_PRODUCT_ID = 0x0001; const char USB_SELF_POWER = 0x80; // Self powered 0xC0, 0x80 bus powered const char USB_MAX_POWER = 50; // Bus power required in units of 2 mA const char HID_INPUT_REPORT_BYTES = 64; const char HID_OUTPUT_REPORT_BYTES = 64; const char USB_TRANSFER_TYPE = 0x03; //0x03 Interrupt const char EP_IN_INTERVAL = 1; const char EP_OUT_INTERVAL = 1; const char USB_INTERRUPT = 1; const char USB_HID_EP = 1; const char USB_HID_RPT_SIZE = 44; /* Device Descriptor */ const struct { char bLength; // bLength - Descriptor size in bytes (12h) char bDescriptorType; // bDescriptorType - The constant DEVICE (01h) unsigned int bcdUSB; // bcdU SB - USB specification release number (BCD) char bDeviceClass; // bDeviceClass - Class Code char bDeviceSubClass; // bDeviceSubClass - Subclass code char bDeviceProtocol; // bDeviceProtocol - Protocol code char bMaxPacketSize0; // bMaxPacketSize0 - Maximum packet size for endpoint 0 unsigned int idVendor; // idVendor - Vendor ID unsigned int idProduct; // idProduct - Product ID unsigned int bcdDevice; // bcdDevice - Device release number (BCD) char iManufacturer; // iManufacturer - Index of string descriptor for the manufacturer char iProduct; // iProduct - Index of string descriptor for the product. char iSerialNumber; // iSerialNumber - Index of string descriptor for the serial number. char bNumConfigurations; // bNumConfigurations - Number of possible configurations } device_dsc = { 0x12, // bLength 0x01, // bDescriptorType 0x0200, // bcdUSB 0x00, // bDeviceClass 0x00, // bDeviceSubClass 0x00, // bDeviceProtocol 8, // bMaxPacketSize0 USB_VENDOR_ID, // idVendor USB_PRODUCT_ID, // idProduct 0x0001, // bcdDevice 0x01, // iManufacturer 0x02, // iProduct 0x00, // iSerialNumber 0x01 // bNumConfigurations }; /* Configuration 1 Descriptor */ const char configDescriptor1[]= { // Configuration Descriptor 0x09, // bLength - Descriptor size in bytes 0x02, // bDescriptorType - The constant CONFIGURATION (02h) 0x29,0x00, // wTotalLength - The number of bytes in the configuration descriptor and all of its subordinate descriptors 1, // bNumInterfaces - Number of interfaces in the configuration 1, // bConfigurationValue - Identifier for Set Configuration and Get Configuration requests 0, // iConfiguration - Index of string descriptor for the configuration USB_SELF_POWER, // bmAttributes - Self/bus power and remote wakeup settings USB_MAX_POWER, // bMaxPower - Bus power required in units of 2 mA // Interface Descriptor 0x09, // bLength - Descriptor size in bytes (09h) 0x04, // bDescriptorType - The constant Interface (04h) 0, // bInterfaceNumber - Number identifying this interface 0, // bAlternateSetting - A number that identifies a descriptor with alternate settings for this bInterfaceNumber. 2, // bNumEndpoint - Number of endpoints supported not counting endpoint zero 0x03, // bInterfaceClass - Class code 0, // bInterfaceSubclass - Subclass code 0, // bInterfaceProtocol - Protocol code 0, // iInterface - Interface string index // HID Class-Specific Descriptor 0x09, // bLength - Descriptor size in bytes. 0x21, // bDescriptorType - This descriptor's type: 21h to indicate the HID class. 0x01,0x01, // bcdHID - HID specification release number (BCD). 0x00, // bCountryCode - Numeric expression identifying the country for localized hardware (BCD) or 00h. 1, // bNumDescriptors - Number of subordinate report and physical descriptors. 0x22, // bDescriptorType - The type of a class-specific descriptor that follows USB_HID_RPT_SIZE,0x00, // wDescriptorLength - Total length of the descriptor identified above. // Endpoint Descriptor 0x07, // bLength - Descriptor size in bytes (07h) 0x05, // bDescriptorType - The constant Endpoint (05h) USB_HID_EP | 0x80, // bEndpointAddress - Endpoint number and direction USB_TRANSFER_TYPE, // bmAttributes - Transfer type and supplementary information 0x40,0x00, // wMaxPacketSize - Maximum packet size supported EP_IN_INTERVAL, // bInterval - Service interval or NAK rate // Endpoint Descriptor 0x07, // bLength - Descriptor size in bytes (07h) 0x05, // bDescriptorType - The constant Endpoint (05h) USB_HID_EP, // bEndpointAddress - Endpoint number and direction USB_TRANSFER_TYPE, // bmAttributes - Transfer type and supplementary information 0x40,0x00, // wMaxPacketSize - Maximum packet size supported EP_OUT_INTERVAL // bInterval - Service interval or NAK rate }; const struct { char report[]; }hid_rpt_desc = { 0x05, 0x01, // USAGE_PAGE (Generic Desktop) 0x09, 0x05, // USAGE (Game Pad) 0xa1, 0x01, // COLLECTION (Application) 0xa1, 0x00, // COLLECTION (Physical) 0x05, 0x09, // USAGE_PAGE (Button) 0x19, 0x01, // USAGE_MINIMUM (Button 1) 0x29, 0x08, // USAGE_MAXIMUM (Button 0x15, 0x00, // LOGICAL_MINIMUM (0) 0x25, 0x01, // LOGICAL_MAXIMUM (1) 0x95, 0x08, // REPORT_COUNT (8) 0x75, 0x01, // REPORT_SIZE (1) 0x81, 0x02, // INPUT (Data,Var,Abs) 0x05, 0x01, // USAGE_PAGE (Generic Desktop) 0x09, 0x30, // USAGE (X) 0x09, 0x31, // USAGE (Y) 0x09, 0x32, // USAGE (Z) 0x15, 0x81, // LOGICAL_MINIMUM (-127) 0x25, 0x7f, // LOGICAL_MAXIMUM (127) 0x75, 0x08, // REPORT_SIZE (8) 0x95, 0x04, // REPORT_COUNT (4) 0x81, 0x02, // INPUT (Data,Var,Abs) 0xc0, // END_COLLECTION 0xc0 // END_COLLECTION }; //Language code string descriptor const struct { char bLength; char bDscType; unsigned int string[1]; } strd1 = { 4, 0x03, {0x0409} }; //Manufacturer string descriptor const struct{ char bLength; char bDscType; unsigned int string[17]; }strd2={ 36, //sizeof this descriptor string 0x03, {'J','O','Y'} }; //Product string descriptor const struct{ char bLength; char bDscType; unsigned int string[14]; }strd3={ 30, //sizeof this descriptor string 0x03, {'F','i','r','s','t','S','u','c','e','s','s'} }; //Array of configuration descriptors const char* USB_config_dsc_ptr[1]; //Array of string descriptors const char* USB_string_dsc_ptr[3]; void USB_Init_Desc(){ USB_config_dsc_ptr[0] = &configDescriptor1; USB_string_dsc_ptr[0] = (const char*)&strd1; USB_string_dsc_ptr[1] = (const char*)&strd2; USB_string_dsc_ptr[2] = (const char*)&strd3; } Ligação no proteus está igual a ligação na ProtoBoard:
  3. Prezados, estou a programar um 12f675 apenas para piscar um led no pino 5, quero que ele fique aceso por 60 segundos e apagado por 500 ms. Não entendo, eu coloco 60000ms no tempo ON e ele não respeita, coloquei 100000ms e deu só 23s marcando num cronômetro. Não quero usar cristal externo. Não preciso de precisão, mas esperraria erro de no máximo 1 segundo + ou -. O que pode estar errado ? Como seria para dar certo ? (estou usando meu velho e funcional gravador JDM com programador PICPgm). Grava com sucesso. Grato /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Definição e inicialização das variáveis * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ # include <12F675.h> #use delay(internal = 4000000) // Oscilador interno de 4MHz #fuses intrc_io #fuses nowdt #fuses put #fuses mclr #fuses nobrownout #fuses noprotect #fuses nocpd /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Definicao das portas * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ #define PINOLED PIN_A2 //Pino ao qual o led esta ligado /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Rotina Principal * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ void main() { while (1) { output_bit(PINOLED, TRUE); // Liga o Led delay_ms(110000); // Ligado por 1 minuto output_bit(PINOLED, FALSE); // Desliga o Led delay_ms(700); // Desligado por meio segundo } }
  4. Estou tentando gravar um código simples de piscar led no PIC18F4550 usando o mplab e o compilador XC8 juntamente com o gravador k150 (fiz o upgrade do firmware), porém quando o arquivo .hex é carregado no microborn os "endereços" ficam com na imagem abaixo (Já consegui gravar o microcontrolador com um arquivo .hex do mikroc). Já tentei exportar o arquivo .hex diretamente, mas fica da mesma forma. Gostaria de saber se isso pode ser alguma incompatibilidade entre o mplab e o k150 ou algum erro que estou cometendo. Segue o código que estou tentando gravar. /* * File: main.c * Author: natan * * Created on 28 de Dezembro de 2019, 10:56 */ // PIC18F4550 Configuration Bit Settings // 'C' source line config statements // CONFIG1L #pragma config PLLDIV = 1 #pragma config CPUDIV = OSC1_PLL2 #pragma config USBDIV = 1 // CONFIG1H #pragma config FOSC = INTOSC_XT #pragma config FCMEN = OFF #pragma config IESO = OFF // CONFIG2L #pragma config PWRT = OFF #pragma config BOR = OFF #pragma config BORV = 3 #pragma config VREGEN = OFF // CONFIG2H #pragma config WDT = OFF #pragma config WDTPS = 32768 // CONFIG3H #pragma config CCP2MX = ON #pragma config PBADEN = ON #pragma config LPT1OSC = OFF #pragma config MCLRE = OFF // CONFIG4L #pragma config STVREN = ON #pragma config LVP = ON #pragma config ICPRT = OFF #pragma config XINST = OFF // CONFIG5L #pragma config CP0 = OFF #pragma config CP1 = OFF #pragma config CP2 = OFF #pragma config CP3 = OFF // CONFIG5H #pragma config CPB = OFF #pragma config CPD = OFF // CONFIG6L #pragma config WRT0 = OFF #pragma config WRT1 = OFF #pragma config WRT2 = OFF #pragma config WRT3 = OFF // CONFIG6H #pragma config WRTC = OFF #pragma config WRTB = OFF #pragma config WRTD = OFF // CONFIG7L #pragma config EBTR0 = OFF #pragma config EBTR1 = OFF #pragma config EBTR2 = OFF #pragma config EBTR3 = OFF // CONFIG7H #pragma config EBTRB = OFF // #pragma config statements should precede project file includes. // Use project enums instead of #define for ON and OFF. #include <xc.h> #define _XTAL_FREQ 8000000 void main(void) { TRISBbits.TRISB0 = 0x00; while(1){ LATBbits.LATB0 = 0x01; __delay_ms(1000); LATBbits.LATB0 = 0x00; __delay_ms(1000); } return; }
  5. #include<12f675.h> #fuses NOMCLR,INTRC_IO,NOPROTECT,NOWDT,NOPUT #use delay (clock=4000000) //#use fast_io(A) void main (){ output_high(pin_a0); while (input(pin_a0)){ //enquanto o pino a1 for igual a = 1 output_high(pin_a1); delay_ms(5000); output_low(pin_a1); } } NÃO ESTÁ FUNCIONANDO POR MAIS SIMPLES QUE SEJA NO PROTEUS
  6. Boa tarde galera, estou com um problema aqui. Tenho uma balança digital de precisão, preciso enviar os dados lidos para um uC, entretanto a balança não tem nenhuma saída (serial/usb), balanças com saídas seriais são bem caras e inviabiliza a ideia, então ai vem a pergunta. Como ler a tela da balança? será que teria como interpretar os números exibidos? a balança é essa, link da imagem hospedada: https://ibb.co/HBDK7hr O que me dizem, será possível?
  7. Boa tarde, preciso fazer uma leitura ad, encontrei esta formula, 1024*470/(470+rn)=100 como calcular rn ? Onde 470 é R, não consigo calcular Rn, alguém poderia me explicar.
  8. Ao apertar um botão uma das fileiras dos LEDS permanece acesa e é ligada a ventoinha, e a velocidade rpm é exibida no painel LCD, o processo funciona por 30 segundos e desliga.Bom eu consegui fazer funcionar a fileira de led e a ventoinha....ai mostra a rpm na tela mas n to conseguindo adicionar um botão para inicializar tudo e nem um tempo de 30 segundos para finalizar o programa, alguém pode me ajudar? PFV PIC18F4520 CRISTAL: 8Mhz ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ // vARIAVEIS GLOBAIS unsigned char ucTexto[10]; // Matriz para armazenamento de texto. unsigned char ucPorcentagem; // Armazena a porcentagem do PWM. unsigned int iLeituraAD = 0; // Define variável para armazenamento da leitura AD. unsigned int iReg_timer1; // Armazena o RPM. // CONFIGURAÇÃO DOS PINOS DO LCD. sbit LCD_RS at RE2_bit; sbit LCD_EN at RE1_bit; sbit LCD_D7 at RD7_bit; sbit LCD_D6 at RD6_bit; sbit LCD_D5 at RD5_bit; sbit LCD_D4 at RD4_bit; // DIREÇÃO DOS PINOS. sbit LCD_RS_Direction at TRISE2_bit; sbit LCD_EN_Direction at TRISE1_bit; sbit LCD_D7_Direction at TRISD7_bit; sbit LCD_D6_Direction at TRISD6_bit; sbit LCD_D5_Direction at TRISD5_bit; sbit LCD_D4_Direction at TRISD4_bit; void interrupt(){ if (INTCON.TMR0IF == 1){ // Se o flag de estouro do TIMER0 for igual a 1, então PORTB.RB0 = ~PORTB.RB0; // Inverte o estado do PORTB.RB0. TMR0L = 0XCB; // Carrega valores de contagem TMR0H = 0XF3; // Carrega valores de contagem INTCON.TMR0IF = 0; // Seta T0IE, apaga flag de entouro do TIMER0 iReg_timer1 = TMR1L*(600/7); // Pega valor lido do timer1 e multiplica por 600 para saber rotação por minuto. // e divide por 7 pois a ventoinha para dar uma volta completa realiza 7 pulsos. TMR1L = 0; // Limpa contador. } } void main(){ TRISB = 0; // Define PORTB como saida. TRISD = 0; // Define PORTD como saida. TRISC.RC0 = 1; // Define PORTC.RC0 como entrada. TRISC.RC2 = 0; // Define PORTC.RC2 como saida. TRISE = 0; // Define PORTE como saida. PORTB = 0; // Limpa PORTB. // Configuração das interrupções INTCON.GIEH = 1; // Habilita as interrupções e a interrupção de alta prioridade. INTCON.GIEL = 1; // Habilita as interrupções e a interrupção de baixa prioridade RCON.IPEN = 1; // Configura 2 niveis de interrupção. // Timer 0 INTCON.TMR0IF = 0; INTCON2.TMR0IP = 1; INTCON.TMR0IE = 1; //temporização de 100 milisegundos T0CON = 0B10000101; // Configura timer modo 16 bits, com prescaler 64 TMR0L = 0XCB; // Carrega valores de contagem TMR0H = 0XF3; // Carrega valores de contagem INTCON.TMR0IF = 0; // Apaga flag de estouro do TIMER0 // Timer 1 para modo contador de pulsos externos T1CON = 0B10000011; // Liga TIMER1 como Contador em RC0, prescaler 1:1, modo 16bits. TMR1L = 0; // Carrega valor de contagem baixa do TIMER1 TMR1H = 0; // Carrega valor de contagem alta do TIMER1 PIR1.TMR1IF = 0; // Apaga flag de estouro do TIMER1 ADCON0 = 0b00000001; // Configura conversor A/D Canal 0, conversão desligada, A/D ligado. ADCON1 = 0b00001110; // Configura todos canais como Digital menos AN0 e REF Interna. ADCON2 = 0b10111110; // Configura conversor A/D para resultado justificado a direita, clock de 20 TAD, clock de Fosc/64. // Config. LCD no modo 4 bits Lcd_Init(); // Inicializa LCD. Lcd_Cmd(_LCD_CLEAR); // Apaga display. Lcd_Cmd(_LCD_CURSOR_OFF); // Desliga cursor. Lcd_Out(1, 1,"velo :"); // Escreve mensagem no LCD PWM1_Init(5000); // Inicializa módulo PWM com 5Khz PWM1_Set_Duty(255); // Seta o Duty-cycle do PWM em 100%. PWM1_Start(); // Inicia PWM. while(1){ // Aqui Definimos Uma Condição Sempre Verdadeira Como Parametro, Portanto Todo O Bloco Será Repetido Indefinidamente. iLeituraAD= ADC_Read(0); // Lê Canal AD 0 iLeituraAD=(iLeituraAD*0.24); // Converte valor para o duty cycle [255/(1023 pontos do A/D)] PWM1_Set_Duty(iLeituraAD); // Envia o valor lido de "iLeituraAD" para o módulo CCP1 PWM iLeituraAD=(iLeituraAD*0.41); // Converte valor para o duty cycle em % WordToStr(iLeituraAD, ucTexto); // Converte o valor lido no A/D em string Lcd_Out(1,11,ucTexto); // Imprime no LCD o valor do Duty Cycle. PORTD=0; PORTB=0; PORTD=255; PORTB=255; WordToStr(iReg_timer1, ucTexto); // Converte o valor lido no iReg_timer1 em string Lcd_Out(2,1,ucTexto); // Imprime no LCD o valor da RPM. Lcd_Out_CP(" RPM"); // Unidade "RPM". Delay_10us; } } <>
  9. Boa tarde a todos, estou com esse código de programação abaixo, porém a dúvida, estou usando pilhas, se deixo a leitura só em um sensor no caso AN0 ele não consome quase nada de pilha, agora se coloco o AN1 cai praticamento um volt por minuto, já fiz o teste trocando as portas continua sempre a mesma coisa, alguém pode me ajudar ou ter alguma ideia do que seja??? leitura do AD do PIC: if (analogico-5 > pilha/2 || analogico+5 < pilha/2) {inicio: { setup_adc_ports(sAN0|sAN1); setup_adc(ADC_CLOCK_INTERNAL); { while(TRUE) { set_adc_channel(0); delay_us(15); analogico = read_adc(); delay_ms(100); set_adc_channel(1); delay_us(15); pilha = read_adc(); delay_ms(100); } if (analogico-5 > pilha/2 || analogico+5 < pilha/2) goto alarme1;
  10. Boa tarde, estou tentando desenvolver um projeto para faculdade no qual tenho que acionar um motor pelo PIC, de tanto em tanto tempo (funciona por 10 segundos e desliga por 1 minuto). Estou com dúvida em como chamar a função de tanto em tanto tempo, daria para fazer criando uma função void delay? ex: void delay(unsigned long int tempo) { unsigned long int x, y; for(x=0; x<100; x++)for (y=0; y<tempo; y++); } void main(void){ while(1) { motor=1 delay(10000) motor=0 delay(60000) } } Valeu!!
  11. gente eu comprei dois modulo desse com 4 matriz 8x8 e 4 max7219 ligado em cascata, mais n consigo entender o funcionamento dele em cascata ja procurei no google e so acho com arduino. Alguem aqui que entende desse bichinho poderia me da ao menos uma luz
  12. galera é o seguinte eu fiz aqui um codigo com o pic 12f629 usando oscilador interno e comunicacao SPI com o max 7219 e 4 displays 7 segmentos. acontece que tem horas quando ligo ou reseto ele trava com todos os digitos acesos, ai so retirando a alimentacao pra funcionar de novo. alguem me ajuda #define contmais button(&gpio, 4, 1, 0) #define contmenos button(&gpio, 5, 1, 0) // Software SPI module connections sbit SoftSpi_SDI at GP0_bit; sbit SoftSpi_SDO at GPIO; sbit SoftSpi_CLK at GP1_bit; sbit SoftSpi_SDI_Direction at TRISIO0_bit; sbit SoftSpi_SDO_Direction at TRISIO; sbit SoftSpi_CLK_Direction at TRISIO1_bit; //MAX7219 sbit MAX7219_CS at GP2_bit; sbit MAX7219_CS_Direction at TRISIO2_bit; int i; /////////////////////////////////////////////// //////////Funções do MAX7219/////////////////// ////////////////////////////////////////////// //Inicia a comunicacao SPI e configura o chip MAX7219 void MAX7219_Init() { MAX7219_CS_Direction = 0; ///pic12f629/// CMCON = 0x07; ////////////// Soft_SPI_Init(); MAX7219_CS = 0; Soft_SPI_write(0x09); Soft_SPI_write(0xff); //Code B MAX7219_CS = 1; //Configura a luminosidade do display(0-15) MAX7219_CS = 0; Soft_SPI_write(0x0a); Soft_SPI_write(0x0f); //intensity MAX7219_CS = 1; //Configura o numero de displays escaneados(0-7) MAX7219_CS = 0; Soft_SPI_write(0x0b); Soft_SPI_write(0x04); //scan limit MAX7219_CS = 1; MAX7219_CS = 0; Soft_SPI_write(0x0c); Soft_SPI_write(0x01); //ShutDown MAX7219_CS = 1; MAX7219_CS = 0; Soft_SPI_write(0x00); Soft_SPI_write(0xff); MAX7219_CS = 1; } //Escreve um numero no display void MAX7219_putNumber(char *number) { char i=0,dp=0; char len = strlen(number); MAX7219_CS = 0; Soft_SPI_write(0x09); Soft_SPI_write(0xff); //CodeB MAX7219_CS = 1; for(i=4; i > 0; i--) { MAX7219_CS = 0; Soft_SPI_Write(i); if(number[len-1]=='.') { dp = 0x80; i++; } else if(number[len-1]==' ' || number[len-1]==0 || len==0) { Soft_SPI_Write(0x7f); dp = 0; MAX7219_CS = 1; } else if(number[len-1]=='-') { Soft_SPI_write(10); dp = 0; MAX7219_CS = 1; } else { Soft_SPI_Write((number[len-1] - '0') | dp); dp = 0; MAX7219_CS = 1; } if(len > 0) len--; } } void main() { unsigned int cont=0; char txt[7]; bit flag1, f1; trisio4_bit=1; gp4_bit=0; MAX7219_Init(); while(1) { //----------mais-------------------------- if((contmais) && flag1 ==1) { flag1=0; } if((!contmais) && flag1 ==0) { flag1=1; cont++; }// fim mais /*------------menos------------------------ if((contmenos) && f1 ==1) { f1=0; } if((!contmenos) && f1 ==0) { f1=1; cont--; }//fim menos */ IntToStr(cont, txt); MAX7219_putNumber(txt); } }
  13. ola pessoal estou precisando de ajuda com esse pll tb31262 ,nao estou conseguindo entender como enviar os dados divisor e frequencia de tx rx como funciona esse protocolo de comunicação ? http://www.datasheetcatalog.com/datasheets_pdf/t/b/3/1/tb31262.shtml obrigado
  14. @Isadora Ferraz Bom Dia: Vamos continuar nossos estudos nesse novo tópico, se você não se importar Surid_7
  15. Olá: Preciso de ajuda para fazer um PIC 16F628 se comunicar (comunicação Serial) com um Arduíno UNO. Não sei programar em C (PIC), apenas em Assembly. Sei programar o Arduíno. Eu consigo fazer um PIC se comunicar com outro (USART), e conheço a comunicação serial do Arduíno. Preciso de umas dicas para fazer o PIC se comunicar com o Arduíno, de forma harmoniosa. Se alguém souber, me ajude. Surid
  16. Estou fazendo um projeto com o PIC18F4520, a programação já está pronta (feita no mikroC for PIC) e testada no Proteus. Pegamos um gravador chamado TS8900-K150, mas, independente do software que usamos para passar a programação, aparece sempre o mesmo erro (Imagem). Alguém saberia solucioná-lo?
  17. Olá Pessoal! Nesse projetinho de pisca led na protoboard não funciona. Fiz no proteus e o mesmo funciona normal, no entanto na protoboard ele não roda. Compilei no mplab carreguei no proteus e funcionou. Fiz no ccs c carreguei no proteus e funcionou. No entanto na protoboar não roda. 5V de alimentação. //Carrega a biblioteca para o microcontrolador PIC16F722a #include <16F722A.h> //configuração FUSES #FUSES NOWDT #FUSES INTRC_IO #FUSES NOPUT #FUSES NOPROTECT #FUSES NOBROWNOUT #FUSES NOMCLR //Define a utilização do clock interno de 16 Mhz #use delay(clock=16000000) #define PINOLED PIN_B1 //Pino ao qual o led esta ligado void main() { while (1) { output_bit(PINOLED, TRUE); // Liga o Led delay_ms(500); // Aguarda 1/2 segundos output_bit(PINOLED, FALSE); // Desliga o Led delay_ms(200); // Aguarda 1/2 segundos } }
  18. ; ; PC-Keyboard emulator using a PIC16F84 ; Scan a 32 key keyboard ( with alternative mapping = 64 keys ) ; ; COPYRIGHT (c)1999 BY Tony K&uuml;bek ; This is kindly donated to the PIC community. It may be used freely, ; and you are forbidden to deprive others from those rights. ; Please read the FSF (Free Software Foundation) GNU statement. ; ;********************************************************************* ; ; E-MAIL tony.kubek@flintab.se ; ; ; DATE 2000-01-23 ; ITERATION 1.0B ; FILE SAVED AS PiCBoard.ASM ; FOR PIC16F84-10/P ; CLOCK 10.00 MHz RESONATOR ( OSC-HS ) ; INSTRUCTION CLOCK 2.50 MHz T= 0.4 us ; SETTINGS WDT-ON, PWT-ON, CP-OFF ; REVISION HISTORY ; 0.1b - First beta, mainly for testing ; 1.0b - First public beta release ; ; ; ; ;*************************************************************************** ; ; PREFACE ;-) ; ; This is NOT an tutorial on pc keyboards in general, there are quite ; a few sites/etc that have that already covered. However i DID find ; some minor ambiguities regarding the actual protocol used but nothing ; that warrants me to rewrite an complete pc keyboard FAQ. ; So for 'general' pc keyboard info ( scancodes/protocol/pin config/etc ) ; here are some useful links: ; ; http://www.senet.com.au/~cpeacock/ ; http://ourworld.compuserve.com/homepages/steve_lawther/keybinfo.htm ; http://204.210.50.240/techref/default.asp?url=io/keyboard.htm ; http://www.arne.si/~mauricio/PIC.HTM ; ; PLEASE do not complain about code implementation, I know there are parts ; in which is it 'a bit' messy and hard to follow, and other parts where ; the code is not optimised. Take it as is. Futhermore I did hesitate ; to include all of the functionality thats currently in, but decided to ; keep most of it, this as the major complaint i had with the other available ; pc-keyboard code was just that - 'is was incomplete'. Also do not ; be discoraged by the size and complexity of it if you are an beginner, ; as a matter of fact this is only my SECOND program ever using a pic. ; I think I managed to give credit were credit was due ( 'borrowed code' ). ; But the originators of these snippets has nothing to do with this project ; and are probably totally unaware of this, so please do not contact them ; if you have problems with 'my' implementation. ; ; BTW ( shameless plug ) UltraEdit rules ! ( if you dont have it, get it ! ) ; http://www.ultraedit.com ( dont 'forget' the 'wordfile' for PIC, color indenting ) ; Without that I guess this file would be 'messy'. ; ; Ok with that out of the way here we go: ; ; ;*************************************************************************** ; DESCRIPTION ( short version ;-) ) ; A set of routines which forms a PC keyboard emulator. ; Routines included are: ; Interrupt controlled clock ( used for kb comm. and 'heart beat ) ; A 4x8 matrix keyboard scanner ( using an 74HCT4051 3 to 8 analogue multiplexer ) ; Communincation with PC keyboard controller, both send end recive. ; PC keyboard routines are using 4 pins (2 inputs & 2 outputs) to control ; keyboard's 2 bidirectional OC lines (CLK & DATA). The following ; 'drawing' conceptually shows how to connect the related pins/lines ; ; ( ASCII art/info shamelessly 'borrowed' from http://www.arne.si/~mauricio/PIC.HTM ) ; ; vcc vcc ; | | ; \ -+- ; / 2K2 /_\ 1N4148 ; \ | ; pcCLOCK_in -----------------o---o------o--------- kbd CLOCK ; | | | ; 2N2222 |50pF -+- ; pcCLOCK_out ____/\/\/\____|/ === /_\ ; 2K2 |\> | | ; | | | ; /// /// /// ; ; An identical circuit is used for the DATA line. ; Note: The 2N2222 transitors can be replaced with BC337 ( NPN ) which are cheaper ; The keyboard matrix routines are using RB4-RB7 as inputs. ; and RB0-RB2 as output to/from an 3 to 8 multiplexer ; so that it can read up to 4x8 keys ( 32 ). ; ; RA4/TOCK1 is an input from an jumper, if low then keyrepeat is disabled and ; alt-key is enabled instead i.e. instead of repeating a key that has ; been depressed a certain amount of time, a bit is set that can change the ; scancode for a key ( of course, all keys can have an alternate scancode ). ; To exit the alt. keymap press the 'enter alt. keymap' key once again or wait until ; timeout. ( defined in the code ) ; NOTE !! The so called 'enter alt. keymap' key is hardcoded, i.e the key ; i've choosen in this 'example' is Column 1 Row 2, if this is to be changed ; code has to be changed/moved in the debounce and checkkeystate routines. ; ( marked with <-------Alt keymap code-------> ) ; RB3 is currently used for a flashing diode. ( running ) ; Note my real keyboard ( KeyTronic ) uses a clock rate of 40 us, my implementation ; uses about 50 us, easily to change. ; ;*************************************************************************** ; DESCRIPTION ( longer version ;-) ) ; ; Pin Used for ; ------------------------------------------------------------ ; RA0 Pc keyboard data out ( to pc ) ; RA1 Pc keyboard data in ( from pc ) ; RA2 Pc keyboard clock in ; RA3 Pc keyboard clock out ; RA4 Flashing disco led 0.5s duty cycle( could be used for some more meaningful purpose ) ; ; RB0 Least significant bit in column adress to 4051 multiplexer ( own keyboard ) ; RB1 Middle... ; RB2 Most significant bit -- || -- ; RB3 Input. Disable keyrepeat & enable alternative keymapping ( if = '0',ground) ; OR if '1' ( free,+V ) enable keyrepeat & disable alternative keymapping ; RB4 Own keyboard input row 1 ; RB5 --- || --- row 2 ; RB6 --- || --- row 3 ; RB7 --- || --- row 4 ; ; 'Basic program structure': ; ; Init - Initialise ports , ram, int, vars ; Start delay - After init the timer int is enabled and the flashing led will ; start to toggle ( flash ). Before I enter the mainloop ; ( and send any keycodes ) I wait until the led has flashed ; twice. This is of course not really needed but I normally ; like to have some kind of start delay ( I know 1 sec is a bit much :-) ) ; Time Int - The timer interrupt (BIG), runs in the background: ; - 'Normal' rate 0.5 ms, when in rx/tx rate is 27 us ; - Performs an rx/tx check, then jumps to rx, tx or 'heart beat' code. ; - TX code sends a byte to pc, at a rate of 27us per int. ; The int rate is actually double the bit rate, as ; a bit is shifted out in the middle of the clock pulse, ; I've seen different implementations of this and I think ; that the bit is not sampled until clock goes low again BUT ; when logging my keyboard ( Keytronic ) this is the way that it ; does it. When all bits are sent, stopbit/parity is sent. ; And the key is removed from the buffer. ; After stopbit/parity is sent, a delay is inserted ( 0.5 ms ) ; before next rx/tx check. ; - RX code recevies a byte from the pc, PIC is controlling clock !! ; Int rate ( 27 us ) is, again, double bit rate. ; Toggles clock and samples the data pin to read a byte from pc. ; When reception is finished an 'handshake' takes place. ; When a byte has been recevied a routine is called to check ; which command and/or data was received. If it was ; keyboard rate/delay or led status byte, it is stored in local ram ; variables. NOTE: The rate/delay is not actually used for ; key repeat as the code is right now ( I use my 'own' fixed rate/delay ) ; however it is very easy to implement. ; After handshake a delay is inserted ( 0.5 ms ) ; before next rx/tx check. ; - 'Heart beat' ( idle code 0.5 ms tick ) performs: ; - Check clock/data lines to see if pc wants to send something or ; if tx is allowed. ; - If tx is possible it checks the keybuffer for an available key and ; if keys are in buffer then it initiates a tx seq. ; and sets the int rate to 27 us. ; - If the pc wants to send something, an rx seq. is initiated ; ( there is some handshaking involved, during which ; the int rate is set to 60 us ) after that, the int rate is ; set to 27 us and an rx seq is started. ; - Divides some clock counters to achive 10ms,100ms,500ms sections. ; - In 100 ms section it performes a numlock status check and ; keyrepeat check ( both rate and delay is local in 100 ms ticks, ; thats why I dont use the 'real' rate delay ) ; - If numlock status is not the desired one code is called to ; toggle the numlock status. ; - If a key has been pressed long enough for repeat, an bit is set ; so we can repeat the key ( send the scancode again ) in the main loop. ; - In 500 ms section the led is toggled on each loop ; - Some various alternative keymap checks to get out of ; alternative keymap. ( i'll get to that in a bit ) ; ; Main loop - Outputs an adress to the 4051 multiplexer waits 1 ms ; reads the row inputs ( 4 bits/keys ), increments address ; and outputs the new adress, waits 1 ms and reads the input ; ( next 4 bits/keys ). Now using the address counter, calls a ; debounce/send routine that first debounces the input, ; ( four consecutive readings before current state is affected ) ; and when a key is changed a make/break code is sent ( put in buffer ). ; In the next loop the next two columns are read etc. until all ; 4 column pairs are read. ; - If keyrepeat is enabled ( see pin conf. above ) the ; repeat flag is checked and if '1' the last pressed key scancode ; is sent again ( put in buffer ). ; - If keyrepeat is not enabled( alternative keymap is enabled instead ) ; then various checks to exit the alternative keymap are performed instead. ; ; Scancodes for all key are located in a lookup table at the end of this file, ; each key has four program rows, to make room for extended codes and alt. keymap codes. ; ; Explanation of 'alternative' keymap: ; ; Using this program ( or an heavily modified version of it anyway :-) ) ; on a computer running Windows posed some small problems; namely: ; -The keyboard ( mapping ) I used did not have any 'special' key such as ; <alt>,<ctrl>,<tab> etc. ; -In windows, things can go wrong, :-) if a dialog pops up or something similar ; there were just no way one could dispose of this with the keymapping i used. ; - 'Only' 28 keys were implemented ( hardware wise ). ; In this particular case the keyrepeat was disabled ( due to the nature of the application ) ; Therefore i came up with the solution to use the keyrepeat related routines and vars. ; To handle a so called 'alternative' keymapping. ; This means that an key is dedicated to be the alt. keymap toggle key, ; when pressing this longer than the programmed repeat delay, instead of ; repeating the key a bit variable is set to use another set of scancodes for ; the keyboard. This 'alternative' keymap is then enabled even though the ; alt. keymap toggle key is released, but it also incorporates an timeout ; that will return to normal keymap if no key is pressed within a certain time ( currently 7 sec ) ; Of course pressing the alt. keymap toggle key again ( while in alt. keymap ) ; will return the keyboard to the normal keymap. ; NOTE !! the key choosen for the alt. keymap toggle is hardcoded, so if the scancode ; for this is changed in the lookup table, changes have to be made in other routines ; as well. ( namly : CHECK_KEY_STATE, and DEBOUNCE_COLUMN routines ) ; While in alt. keymap each key CAN have an alternative scancode ( see lookup table ). ; Also by using the numlock bit, one can toggle numlock status 'on the fly' when entering ; or exiting the alt keymap. ; ; Some notes about the local keyboard interface ( matrix ): ; Although the hardware circuit and software allows virtually unlimited ; simultaneosly pressed keys, the keyboard matrix itself normally poses ; some limitations on this. If an keymatrix without any protective diodes ; are used then one would have loops INSIDE the keymatrix itself when ; multiple keys are pressed in different columns . ; Look at the ( although horrible ;-) ) ASCII art below(internal weak pullup enabled): ; 0 - Key is free ; 1 - Key is pressed ( connection between hor/ver rows ) ; Three keys pressed adressing ( reading ) left column : ; ; To pic1 --------0-------0------ ( row 1 ) ; | | ; To pic2 --------1-------1------ ( row 2 ) ; | | ; To pic3 --------1-------0------ ( row 3 ) ; | | ; Column(4051) 0V - ( Current column is set to 0V when adressing ) ; ; This works as intended, we can read a '0' on pic inputs 2,3 which ; is what we expected. The current ( signal ) follows the route marked with '*': ; ( only the 'signal path' is shown for clarity ) ; ; To pic1 --------0-------0------ ( row 1 ) ; | | ; To pic2 *********-------1------ ( row 2 ) ; * | ; To pic3 *********-------0------ ( row 3 ) ; * | ; Column(4051) 0V - ( Current column is set to 0V when adressing ) ; ; However, when we now read ( address ) the right column instead we ; do not read what is expected ( same three keys still pressed ): ; ; To pic1 --------0-------0------ ( row 1 ) ; | | ; To pic2 *****************------ ( row 2 ) ; *<- * ; To pic3 *********-------*------ ( row 3 ) ; | * ; Column(4051) - 0V ( Current read column is set to 0V when adressing ) ; ; As you can see the 'signal' travels in the keymatrix itself ( where the '<-' is )and ; will cause a 'ghost' signal to be read on the pic. So instead ; of having an '0' on input 2 only we also can read an '0' on input 3. ; This is because the two keys in column 1 are interconnected ( when they are pressed ). ; Keep this in mind if you are planning to support multiple pressed keys. ; ; ;*************************************************************************** ; ; Some suggestions for 'improvements' or alternations ; ; - Using the jumper 'disable-repeat' as a dedicated key for switching ; to alternative keymapping. ; - Enable repeat in alternative keymapping ; - Clean up TX/RX code ( a bit messy ) ; - Using the led output ( or jumper input ) as an extra adress line ; to the multiplexers ( in this case 2 pcs. 74HCT4051 ) we could have ; 4x16 keys instead. Would require some heavy modifications though ; as there are not much ram/program space left. But if alternative ; keymapping is discarded ( most likely if one has 64 keys ) each ; key in the lookup table only needs to be 2 lines instead of 4. ; That would 'only' require some modifications to preserv ram. ; - Using the EERAM for 'macros' or similar ( not used at all now ) ; ;*************************************************************************** ; ; LEDGEND ; ; I tend to use the following when naming vars. etc. : ; ( yes i DO like long names ) ; ; For 'general' purpose pins: ; ; An input pin is named I_xxx_Name where : ; ; I_ - This is an input pin ;-) ; xxx_ - Optional what type of input, jmp=jumper etc. ; Name - Self explanatory ; ; An output pin is named O_xxx_Name where: ; ; O_ - This is an output pin ;-) ; xxx_ - Optional what type of output, led=LED etc. ; Name - Self explanatory ; ; Application(function) specific pins: ; ; An application(function) specific pin is named xxName where: ; ; xx - What/Where, for example pc=To/From pc ; Name - Self explanatory ( what does it control etc ) ; ; An #define/constant/equ (no pin or ram ) uses all capital letters e.x. #define BREAK 0xF0 ; ; A bit variable will always start with '_'. For example '_IsLedStatus' ; ; All other ( mostly ramvars. ) are named in various ways. ; ; ;*************************************************************************** TITLE "PC Keyboard emulator - By Tony K&uuml;bek" Processor 16F84 Radix DEC EXPAND ;***** HARDWARE DEFINITIONS ( processor type include file ) INCLUDE <C:\PROGRAM\MPLAB\P16F84.INC> ; this might need changing ! ;***** CONFIGURATION BITS __CONFIG _WDT_ON&_HS_OSC&_CP_OFF&_PWRTE_ON ; _WDT_OFF __IDLOCS 010Bh ; version 1.0B ;***** CONSTANT DEFINITIONS CONSTANT BREAK = 0xF0 ; the break key postfix ( when key is released ) CONSTANT EXTENDED = 0xE0 ; the extended key postfix ; As i dont really use the rate/delay I receive from the pc ( easy to change ) ; this is the current rate/delay times i use: CONSTANT DELAY_ENTER_ALTKEYMAP = 0x1E ; x100 ms , approx 3 seconds ( 30 x 100 ms ) ; how long the 'enter altkeymap' key must ; be in pressed state before the altkeymap is enabled CONSTANT DELAY_EXIT_ALTKEYMAP = 0x0F ; x0.5 sec , approx 7.5 sec ; how long before we exit the alt keymap if no key is ; pressed. CONSTANT DELAY_REPEAT = 0x08 ; x100 ms, approx 800 ms ; how long before we START repeating a key CONSTANT DELAY_RATE = 0x02 ; x100 ms, approx 200 ms repeat rate ; how fast we are repeating a key ( after the delay above ) ;***** CONSTANT DEFINITIONS ( pins ) ; For connection with PC keyboard #define pcCLOCK_in PORTA,2 ; Input PC keyboard clock #define pcCLOCK_out PORTA,3 ; Output PC keyboard clock #define pcDATA_in PORTA,1 ; Input PC keyboard data #define pcDATA_out PORTA,0 ; Output PC Keyboard data ; For connection (input) with our own keyboard ; Note I actually dont use these (definitions!) in the program, but they might come in handy ; at one time or another ;-) ( I use the pins though.. ) #define kbROW_1 PORTB,7 #define kbROW_2 PORTB,6 #define kbROW_3 PORTB,5 #define kbROW_4 PORTB,4 ; Indications ( output ) #define O_led_KEYCOMM_ok PORTA,4 ; communication seems ok led ( flashing ) ; Disable/enable key repeat input jumper #define I_jmp_NoRepeat PORTB,3 ; note: internal weak pullup enabled ; For keybuffer ( using the indirect file selector FSR ) #define KeyBufferHead FSR ;***** RAM ASSIGNMENT CBLOCK 0x0C KeyBufferTail ; where the last byte in buffer is.. clkCount ; used for clock timing Offset ; used for table reads Saved_Pclath ; Saved registers during interrrupt Saved_Status ; ----- Saved_w ; ----- CurrKey ; current key ( rx or tx ).. KeyParity ; key parity storage ( inc. for every '1' ) Divisor_10ms ; for the timer Divisor_100ms ; ditto Divisor_500ms ; Divisor_Repeat ; timer for repeated key sends Flags ; various flags RepeatFlags ; flags for repeating a key bitCount ; bitcounter for tx/rx Comm_Flags ; flags used by both rx and tx routines Temp_Var ; temp storage, can be used outside int loop TRX_Flags ; flags used by both rx and tx routines CommandData ; bit map when receving data bytes from pc ; for example led status/ delay / etc KbLedStatus ; to store status led bitmap for keyboard ( pc first sends 'ED' then this byte ) ; bit 0=Scroll lock ( 1=on ) ; bit 1=Num lock ; bit 2=Caps lock ; bits 3-7 = unused KbRateDelay ; to store repeat delay/rate ( pc first sends 'F3' then this byte ) ; bit 0-4 (rate) = '00000' is 30x/sec '11111' is 2x/sec ( i.e. value * 33 ms ) ; bit 5-7 (delay)= '00' is 250 ms, '11' is 1000 ms ( ie. value * 250 ms ) ; bit 7 = unused BufTemp ; temp byte for storing scancode to put in buffer Temp ; temp byte, used locally in buffer routine Temp2 ; LastKey ; stores the last sent key KbBufferMin ; where our keybuffer starts Kb1 ; used in keybuffer Kb2 ; used in keybuffer Kb3 ; used in keybuffer Kb4 ; used in keybuffer Kb5 ; used in keybuffer Kb6 ; used in keybuffer Kb7 ; used in keybuffer Kb8 ; used in keybuffer Kb9 ; used in keybuffer Kb10 ; used in keybuffer KbBufferMax ; end of keybuffer TempOffset ; temporary storage for key offset ( make/break ) LastMakeOffset ; storage of last pressed key ( offset in table ) RepeatTimer ; timer to determine how long a key has been pressed RepeatKey ; the key to repeat repTemp ; temporary storage in repeat key calc. repKeyMap ; bit pattern for the column in which the repeat key is in ; i.e. a copy of kbColumnXX_Old where 'XX' is the column LastKeyTime ; counter when last key was pressed, used to get out of altkeymap ; after a specific 'timeout' kbScan ; scan code for pressed/released key kbTemp ; temp storage for key states kbState ; which keys that has changed in current columns kbBitCnt ; bit counter for key check ( which key/bit ) kbColumnCnt ; column counter ( loops from 8 to 0 ) ; Used as output to multiplexer/decoder and in debounce routines kbColumnVal ; current value of the last 2 columns ( 2x4bits = 8 bits ) input pins ( keys ) ; ; Note the kbColumnXX_New variables is not really needed ; used it while making the program ( debugging ;-) ). ; so if more free ram is needed change code using these to use ; the current 'input' sample instead. ( kbColumnVal ) kbColumn12_New ; New debounced reading for column 1 & 2 kbColumn12_Old ; Latest known valid status of column 1 & 2 kbColumn12Cnt ; Debounce counter for column 1 & 2 kbColumn12State ; State of debounce for column 1 & 2 kbColumn34_New ; New debounced reading for column 3 & 4 kbColumn34_Old ; Latest known valid status of column 3 & 4 kbColumn34Cnt ; Debounce counter for column 3 & 4 kbColumn34State ; State of debounce for column 3 & 4 kbColumn56_New ; New debounced reading for column 5 & 6 kbColumn56_Old ; Latest known valid status of column 5 & 6 kbColumn56Cnt ; Debounce counter for column 5 & 6 kbColumn56State ; State of debounce for column 5 & 6 kbColumn78_New ; New debounced reading for column 7 & 8 kbColumn78_Old ; Latest known valid status of column 7 & 8 kbColumn78Cnt ; Debounce counter for column 7 & 8 kbColumn78State ; State of debounce for column 7 & 8 ENDC ;******* END RAM ; *** flags used by both rx and tx routines #define _isParity Comm_Flags,0 ; bit in rx/tx is parity bit #define _KeyError Comm_Flags,1 ; set to '1' when an error is detected #define _isStartBit Comm_Flags,2 ; set to '1' when bit in rx/tx is startbit #define _isStopBit Comm_Flags,3 ; --- || --- but stopbit #define _RxCanStart Comm_Flags,4 ; for handshaking, when negotiating an rx seq. ; *** dito used for rx and tx #define _KeyReceived TRX_Flags,0 ; rx #define _RX_Mode TRX_Flags,1 ; rx is in progress ( started ) #define _doRXAck TRX_Flags,2 ; do rx handshake #define _RXAckDone TRX_Flags,3 ; rx handshake is done #define _RXEnd TRX_Flags,4 ; rx seq is finished #define _RXDone TRX_Flags,5 ; rx exit bit #define _KeySent TRX_Flags,6 ; tx key has been succesfully sent #define _TX_Mode TRX_Flags,7 ; tx is in progress ( started ) ; *** flags to determine the meaning of an incomming data is ; i.e. when then pc has sent a 'data follow' instruction ; these bits are set according to the command, ; i.e. the next incomming byte is..... #define _IsLedStatus CommandData,0 ; the next incoming byte contains kb led status #define _IsRateDelay CommandData,1 ; the next incoming byte contains kb rate/delay #define _SkipByte CommandData,2 ; other data not saved ; *** bit meaning in KbLedStatus ( the leds on the keyboard ) ; i.e local ram copy of this byte #define _LedScrollLock KbLedStatus,0 ; '1' led is on #define _LedNumLock KbLedStatus,1 ; #define _LedCapsLock KbLedStatus,2 ; #define _IsFirstLedStatus KbLedStatus,7 ; set this to '1' at startup, to know that our local ; ; copy (led status) is not yet syncronised. Used to ; ; 'force' sync. the first time we set the local numlock bit. ; *** flags used for various purposes #define _Timer Flags,0 ; used for waiting ;#define _WrongPar Flags,1 ; ?? ( used during dev. to force wrong parity sends, removed ) #define _LastColumn Flags,2 ; for kb scan code #define _isBreak Flags,3 ; if '1' the currently handled key is break ( released ), else make ( pressed ) ;#define _isFree Flags,4 ; Not used ! ( temporary during dev. ) #define _AltKeymap Flags,5 ; is we use alternative keymap ( other scancodes ) see below ! ; enabled ONLY when keyrepeat is disabled ( I_jmp_NoRepeat (RB3) is low ) #define _ExitAltKeymap Flags,6 ; start checking if we should exit alternative keymap ; if all keys are 'free' ( not pressed ) we exit altkeymap #define _InAltKeymap Flags,7 ; if we are waiting for second press/release to get out of altkeymap #define _doRepeat RepeatFlags,0 ; if the last pressed key should be 'repeated' #define _doSendKey RepeatFlags,1 ; send the key in RepeatKey to pc #define _isExtended RepeatFlags,2 ; temp flag if last key is extended #define _RepeatIsExt RepeatFlags,3 ; the key to repeat is extended key #define _startRepeat RepeatFlags,4 ; start repeat timer #define _DoExitAltKeymap RepeatFlags,5 ; bit set when we are getting out of alternative keymap #define _NumLock RepeatFlags,6 ; 'mirror' of numlockstatus, by setting/clearing this bit ; numlock status will be changed. ; I.e. there is no need to 'manually' send break/make code for numlock ; key, by setting this bit to '1' numlock status will by automaticlly ; ( inside int handler ) set to 'on'( within 100 ms ). Se code for example. #define _WaitNumLock RepeatFlags,7 ; bit set when we have sent make/break numlock scancode ; and waiting for numlock status byte reply. ; ( to inhibit a new numlock scancode send ) ;************************************************************************** ; Macros ;************************************************************************** ;+++++ ; BANK0/1 selects register bank 0/1. ; Leave set to BANK0 normally. BANK0 MACRO BCF STATUS,RP0 ENDM BANK1 MACRO BSF STATUS,RP0 ENDM ;+++++ ; PUSH/PULL save and restore W, PCLATH and STATUS registers - ; used on interrupt entry/exit PUSH MACRO MOVWF Saved_w ; Save W register on current bank SWAPF STATUS,W ; Swap status to be saved into W BANK0 ; Select BANK0 MOVWF Saved_Status ; Save STATUS register on bank 0 MOVFW PCLATH MOVWF Saved_Pclath ; Save PCLATH on bank 0 ENDM PULL MACRO BANK0 ; Select BANK0 MOVFW Saved_Pclath MOVWF PCLATH ; Restore PCLATH SWAPF Saved_Status,W MOVWF STATUS ; Restore STATUS register - restores bank SWAPF Saved_w,F SWAPF Saved_w,W ; Restore W register ENDM ;+++++ ; We define a macro that will switch an output pin on or off depending ; on its previous state. We must be on bank0 !! ; TOGGLE_PIN MACRO WHICH_PORT,WHICH_PIN LOCAL TOGGLE_PIN10, TOGGLE_END BTFSC WHICH_PORT,WHICH_PIN ; is the pin high ? GOTO TOGGLE_PIN10 ; yes, clear it BSF WHICH_PORT,WHICH_PIN ; no, so set it GOTO TOGGLE_END TOGGLE_PIN10: BCF WHICH_PORT,WHICH_PIN ; clear the pin TOGGLE_END: ENDM ;************************************************************* ; Credit for routine ( almost unchanged, expect it's now a macro ;-) ) ; goes to Scott Dattalo http://www.interstice.com/~sdattalo/technical/software/software.html ; ( debouce routine at http://www.interstice.com/~sdattalo/technical/software/pic/debounce.html ) ; ; DEBOUNCE_BYTE - 'debounces' 8 bits, when a bit state ; has been 'active' for 4 consecutive debounce loops ; it's state is put into the 'debounced' sample ( i.e. output ) ; ; The purpose of this routine is to debounce, i.e. digitally low pass filter ; inputs. The algorithm handles upto 8 bits at a time. An input is considered ; filtered if it has not changed states in the last 4 samples. ; ; 2-bit cyclic vertical counters count the 4 samples. As long as there is no ; change, the counters are held in the reset state of 00b. When a change is detected ; between the current sample and the filtered or debounced sample, the counters ; are incremented. The counting sequence is 00,01,10,11,00... When the counters ; roll over from 11b to 00b, the debounced state is updated. If the input changes ; back to the filtered state while the counters are counting, then the counters ; are re-initialized to the reset state and the filtered state is unaffected. ; In other words, a glitch or transient input has been filtered. ; ; Here's the C-psuedo code: ; ;static unsigned clock_A,clock_B,debounced_state ; ;debounce(unsigned new_sample) ;{ ; unsigned delta; ; ; delta = new_sample ^ debounced_state; //Find all of the changes; ; clock_A ^= clock_B; //Increment the counters ; clock_B = ~clock_B; ; ; clock_A &= delta; //Reset the counters if no changes ; clock_B &= delta; //were detected. ; ; //Preserve the state of those bits that are being filtered and simultaneously ; //clear the states of those bits that are already filtered. ; debounced_state &= (clock_A | clock_B); ; //Re-write the bits that are already filtered. ; debounced_state |= (~(clock_A | clock_B) & new_sample); ;} ; ; The 2-bit counters are arranged "vertically". In other words 8 counters ; are formed with 2 bytes such that the corresponding bits in the bytes are ; paired (e.g. MSBit of each byte is paired to form one counter). ; The counting sequence is 0,1,2,3,0,1,... And the state tables and Karnaugh ; maps are: ; ;State Table: Karnaugh Maps: ;pres next B ; SS SS 0 1 ; AB AB +---+---+ +---+---+ ;-------- A 0| | 1 | | 1 | | ; 00 01 +---+---+ +---+---+ ; 01 10 1| 1 | | | 1 | | ; 10 11 +---+---+ +---+---+ ; 11 00 A+ = A ^ B B+ = ~B ; ; Here's the PIC code that implements the counter: ; MOVF SB,W ;W = B ; XORWF SA,F ;A+ = A ^ B ; COMF SB,F ;B+ = ~B ; 14 instructions ; 15 cycles ; Inputs: ; NewSample - The current sample ; Outputs ; DebouncedSample - The current value (filtered version of NewSample) ; ; VARS used ; DBCnt, ; DBState - State variables for the 8 2-bit counters ; DEBOUNCE_BYTE MACRO NewSample,DebouncedSample,DBCnt,DBState ;Increment the vertical counter MOVF DBState,W XORWF DBCnt,F COMF DBState,F ;See if any changes occurred MOVF NewSample,W XORWF DebouncedSample,W ;Reset the counter if no change has occurred ANDWF DBState,F ANDWF DBCnt,F ;Determine the counter's state MOVF DBState,W IORWF DBCnt,W ;Clear all bits that are filtered-or more accurately, save ;the state of those that are being filtered ANDWF DebouncedSample,F XORLW 0xff ;Re-write the bits that haven't changed. ANDWF NewSample,W IORWF DebouncedSample,F ENDM ;************************************************************************** ; Program Start ;************************************************************************** ; Reset Vector ORG H'00' ; For the sole purpose of squeezing every last byte of the programming mem ; I actually use the 3 program positions before the interrupt vector ; before jumping to the main program. Take note though that ; ONLY 3 instructions are allowed before the jump to main loop !! BANK0 CLRF PCLATH CLRF INTCON GOTO INIT ;************************************************************************** ; Interrupt routine ; An humongously big int handler here ;-) ; but the keyboard handling is FULLY in the background, without any intervention ; in the main loop whatsoever. I like the solution anyway. ; ;************************************************************************** ; Interrupt vector ORG H'04' INT PUSH ; Save registers and set to BANK 0 BTFSS INTCON,T0IF ; check if TMR0 interrupt GOTO INTX ; whoops ! 'unknown' int, should be disabled... ; NOTE ! if an 'unknown' int triggers the int routine ; the program will loop here for ever ;-) ( as the calling flag is not cleared ) ;+++ ; Timer (TMR0) timeout either heart beat or tx/rx mode ; In 'heart beat mode' we monitor the clock and data lines ; at ( roughly )= 0.5 ms interval, we also check the send buffer ; if there are any keys to send to pc ( if clock/data levels allows us to ) ; In tx/rx mode we are controlling the clock/data line = 27 us tick ; Note: This software works using both 8Mhz and 10Mhz resonators without modifications ; however the 'timing' will then of course be 'off'. INT_CHECK BCF INTCON,T0IF ; Clear the calling flag ! BTFSC _TX_Mode ; check if we are in tx mode GOTO INT_TX ; yep, goto tx mode code.. BTFSC _RX_Mode ; are we in rx mode ? GOTO INT_RX ; yep goto rx mode code GOTO INT_HEART_BEAT ; nope just goto 'heart beat' mode ;*************** TX code start *********************************** INT_TX MOVLW H'FA' ; preset timer with 252 ( 256 - 6 = 250 ) ; timetick = 0.4uS x 8 ( prescale ) x 6 + 8us ( int handling code )= 27 us MOVWF TMR0 BTFSS clkCount,0 ; check if we should toggle clock GOTO INT_DEC_CLOCK ; bit low,decrement and check if we should toggle data instead DECFSZ clkCount,F ; decrement and check if we are at zero.. GOTO INT_CLOCK ; not zero then toggle clock line GOTO INT_EXIT_TX INT_CLOCK BTFSC pcCLOCK_out ; check if we are low GOTO INT_CLOCK_HIGH ; yep set to high BTFSS pcCLOCK_in ; check if pc is pulling the clock line low ; i.e. it wants to abort and send instead.. GOTO INT_TX_CHECK_ABORT ; abort this transfer BSF pcCLOCK_out ; ok to set clock low ( pull down ) GOTO INTX INT_CLOCK_HIGH BCF pcCLOCK_out ; set high ( release line ) ;BCF _ClockHigh ; GOTO INTX INT_TX_CHECK_ABORT GOTO INT_EXIT_TX INT_DEC_CLOCK DECF clkCount,F ; decrement clock counter ( so we toggle next time ) INT_DATA BTFSS bitCount,0 ; check bit counter GOTO INT_DATA_IDLE ; no data toggle DECFSZ bitCount,F ; decrement bit counter GOTO INT_DATA_NEXT ; next bit.. INT_NO_BITS BSF bitCount,0 ; just in case ( stupid code, not sure its needed, just ; to make it impossible to overdecrement )*** BTFSC _isParity ; are we sending parity ? GOTO INT_DATA_END ; exit ; all bits sent ; delete the last key from the buffer CALL INC_KEY_HEAD ; remove the ( last ) key form the buffer as is was sent ok.. ; all bits sent check parity BSF _isParity ; set flag data is parity BTFSS KeyParity, 0 ; is the last parity bit high? ( odd num of bits ) ; then parity should be high ( free ) GOTO INT_DATA_HIGH_PAR ; yes BSF pcDATA_out ; no, parity should be 'low' ( pulled down ) GOTO INTX ; INT_DATA_HIGH_PAR: BCF pcDATA_out ; set parity bit high ( release data line ) GOTO INTX ; and exit.. INT_DATA_END BTFSS _isStopBit ; is the stopbit sent ? GOTO INT_DATA_STOPB ; nope then set stopbit flag BCF pcDATA_out ; parity bit sent, always release data line ( stop bit ) GOTO INTX INT_DATA_STOPB BSF _isStopBit ; set the stopbit flag GOTO INTX ; and exit INT_DATA_IDLE DECF bitCount,F ; decrement bit counter GOTO INTX ; no toggle of data line INT_DATA_NEXT BTFSS CurrKey,0 ; is the last bit of the key_buffer high? GOTO INT_DATA_LOW ; no, pull data low BCF pcDATA_out ; yes, release data line INCF KeyParity,F ; increment parity bit GOTO INT_DATA_ROTATE ; rotate data INT_DATA_LOW ; last bit is low BSF pcDATA_out ; set the bit INT_DATA_ROTATE RRF CurrKey,F ; rotate right by 1 bit GOTO INTX INT_EXIT_TX ; setup the timer so we accomplish an delay after an tx seq BCF _TX_Mode ; clear tx mode flag BCF pcCLOCK_out ; release clock line BCF pcDATA_out ; and data line ; MOVLW H'64' ; start timer with 100 ( 256-100 = 156 ) ; timetick = 0.4uS x 8 ( prescale ) x 156 = (roufly) 0.5 mS MOVWF TMR0 GOTO INTX ;************** TX code end ***************** ;************** RX code start *************** INT_RX MOVLW H'FA' ; preset timer with 252 ( 256 - 6 = 250 ) ; timetick = 0.4uS x 8 ( prescale ) x 6 + 8us ( int handling code )= 27 us MOVWF TMR0 BTFSS clkCount,0 ; check if we should toggle clock GOTO INT_RX_DEC_CLOCK ; bit low,decrement and check if we should read data instead DECFSZ clkCount,F ; decrement and check if we are at zero.. GOTO INT_RX_CLOCK ; not zero then toggle clock line BCF pcCLOCK_out ; release the clock line we are done.. BCF _RX_Mode ; clear rx mode bit ( go over to heart beat mode ) GOTO INT_EXIT_RX INT_RX_CLOCK BTFSC pcCLOCK_out ; check if we are low GOTO INT_RX_CLOCK_HIGH ; yep set to high ( release line ) BTFSC _isStartBit ; check if this is the first bit ( start ) GOTO INT_RX_START ; clear start bit and continue BTFSC _isParity ; check if this is the parity bit ( or parity has been received ) GOTO INT_RX_PAR ; yep check parity GOTO INT_RX_BIT ; ok just a 'normal' bit read it INT_RX_PAR ; check parity BTFSC _doRXAck ; check the handshake flag GOTO INT_RX_HNDSHK ; start handshake check BTFSS pcDATA_in ; is the input high ? GOTO INT_RX_PAR_HIGH ; yep BTFSC KeyParity,0 ; is the parity '0' ( should be ) GOTO INT_RX_PAR_ERR ; nope parity error GOTO INT_RX_ACK ; parity ok next should be ack ( we take data line low ) INT_RX_PAR_HIGH BTFSS KeyParity,0 ; check that parity bit is '1' GOTO INT_RX_PAR_ERR ; nope parity error GOTO INT_RX_ACK ; parity ok, next is ack ( we take data line low ) INT_RX_PAR_ERR BSF _KeyError ; set error flag INT_RX_ACK BSF pcCLOCK_out ; ok to set clock low ( pull down ) BSF _doRXAck ; enable ack check GOTO INTX INT_RX_HNDSHK BTFSS _RXEnd ; if we are done dont take data low BSF pcCLOCK_out ; ok to set clock low ( pull down ) BTFSC _RXAckDone ; chek if hand shake ( ack ) is done ? BSF _RXEnd ; ok we are now done just make one more clock pulse GOTO INTX ; exit INT_RX_CLOCK_HIGH BCF pcCLOCK_out ; set high ( release line ) BTFSS _RXAckDone ; are we done.. ? GOTO INTX BTFSS _RXDone ; finished ? GOTO INTX BCF _RX_Mode ; and clear rx flag.. GOTO INT_EXIT_RX ; bye bye baby INT_RX_DEC_CLOCK DECF clkCount,F ; decrement clock counter ( so we toggle next time ) BTFSS _doRXAck ; check if we are waiting for handshake GOTO INTX BTFSC pcCLOCK_out ; check if the clock is low ( pulled down ) GOTO INTX ; nope we are pulling down then exit ; we only take over the data line if ; the clock is high ( idle ) ; not sure about this though.. ??? BTFSC _RXEnd ; are we done ? GOTO INT_RX_END ; handshake check if data line is free ( high ) BTFSS pcDATA_in ; is data line free ? GOTO INTX ; nope BSF pcDATA_out ; takeover data line BSF _RXAckDone ; we are done..at next switchover from low-high we exit GOTO INTX ; INT_RX_END BCF pcDATA_out ; release data line BSF _RXDone ; we are now done GOTO INTX INT_RX_START BCF _isStartBit ; clear start bit flag BSF pcCLOCK_out ; ok to set clock low ( pull down ) GOTO INTX INT_RX_BIT BCF CurrKey,7 BTFSS pcDATA_in ; is bit high GOTO INT_RX_NEXT ; nope , it's a '0' BSF CurrKey,7 ; set highest bit to 1 INCF KeyParity,F ; increase parity bit counter INT_RX_NEXT BSF pcCLOCK_out ; ok to set clock low ( pull down ) DECFSZ bitCount,F ; decrement data bit counter GOTO INT_RX_NEXT_OK BSF bitCount,0 ; just in case ( so we cannot overdecrement ) BSF _isParity ; next bit is parity GOTO INTX INT_RX_NEXT_OK CLRC ; clear carry, so it doesnt affect receving byte RRF CurrKey,F ; rotate to make room for next bit GOTO INTX ; and exit INT_EXIT_RX ; handle the recevied key ( if not it is an 'data' byte ) MOVLW H'C4' ; preset timer with 196 ( 256 - 60 = 196 ) ; timetick = 0.4uS x 8 ( prescale ) x 60 + 8 ( int handling code )= 200 us ; MOVWF TMR0 ; this delay seems to be needed ( handshake ? ) ; check if this is an data byte ( rate/delay led status etc ) MOVF CommandData,F ; reload into itself ( affect zero flag ) BTFSS STATUS,Z ; check zero flag GOTO INT_STORE_DATA ; byte contains data ( rate/delay etc ) CALL CHECK_RX_KEY ; no data, handle recevied command GOTO INTX INT_STORE_DATA ; store data byte in 'currkey', ; first reply with 'ack' MOVLW H'FA' ; keyboard ack CALL ADD_KEY ; BTFSS _IsLedStatus ; is it led status byte ? GOTO INT_STORE_RATE ; nope check next INT_STORE_NUM ; byte in 'currkey' is led status byte, store it MOVF CurrKey,W ; get byte MOVWF KbLedStatus ; and store it BTFSC _WaitNumLock ; was this something we were waiting for ? ; i.e. we sent the make scancode for numlock. CALL RELEASE_NUMLOCK ; yep, then send release code for 'soft' numlock GOTO INT_STORE_EXIT ; store it in local ram copy and exit INT_STORE_RATE BTFSS _IsRateDelay ; is it rate/delay byte ? GOTO INT_STORE_EXIT ; nope then send ack end exit ; byte in 'currkey' is rate/delay byte, store it MOVF CurrKey,W ; get byte MOVWF KbRateDelay ; and store it INT_STORE_EXIT CLRF CommandData ; clear data byte flags GOTO INTX ;******************* 'heart' beat code ( 0.5 ms tick ) ********* ; Note: As I 'mess' with the int timer this 'heart beat' is by no means ; an accurate clock. However it can work as a rough estimate for local time. ; INT_HEART_BEAT MOVLW H'64' ; start timer with 100 ( 256-100 = 156 ) ; timetick = 0.4uS x 8 ( prescale ) x 156 = (roufly) 0.5 mS MOVWF TMR0 INT_CHECK_CLKDTA: ; CLOCK DATA Action ;-----------+-------- ; L L | wait ? ; L H | wait, buffer keys ; H L | start an rx sequence ; H H | keyboard can tx BTFSS pcDATA_in ; is the data line high ( free ).. GOTO INT_CHECK_RX ; Nope it's pulled down, check if rx is requested BTFSC pcCLOCK_in ; Is the clk line low ( pulled down ) ? GOTO INT_CHECK_BUFF ; Nope, so check if we have any keys to send GOTO INT_IDLE ; clock is low , wait and buffer keys( i.e. no rx/tx ) INT_CHECK_RX ; pc ( probably ) wants to send something.. BTFSS pcCLOCK_in ; wait until clock is released before we go into receving mode.. GOTO INT_RX_IDLE ; nope still low ; clock now high test if we are set to start an rx seq. BTFSS _RxCanStart ; have we set the flag ? GOTO INT_WAIT_RX ; nope then set it BTFSC pcDATA_in ; make sure that data still is low GOTO INT_ABORT_RX ; nope abort rx req, might been a 'glitch' ; initiate the rx seq. CLRF Comm_Flags ; used by both tx/rx routines ( and _RxCanStart bit !! ) CLRF TRX_Flags ; clear tx/rx flags CLRF KeyParity ; clear parity counter BSF _RX_Mode ; set rx mode flag.. BSF _isStartBit ; set that next sampling is start bit ; preset bit and clock counters MOVLW H'2F' ; = 47 dec, will toggle clock output every even number until zero MOVWF clkCount ; preset clock pulse counter MOVLW H'08' ; = 8 dec, number of bits to read ; then parity bit will be set instead MOVWF bitCount ; preset bit counter ; note as we are starting the clock here we allow a longer time before we start ; the actual 20 us tick, this the first time we wait about 200 us before the first clock 'tick' MOVLW H'C4' ; preset timer with 196 ( 256 - 60 = 196 ) ; timetick = 0.4uS x 8 ( prescale ) x 60 + 8us ( int handling code )= 200 us ; MOVWF TMR0 GOTO INTX ; exit, the next int will start an rx seq INT_WAIT_RX: BSF _RxCanStart ; set flag so we start rx next int ( 0.5 ms ) INT_RX_IDLE ; reload clock so we check more often MOVLW H'F0' ; start timer with 240 ( 256-16 = 240 ) ; timetick = 0.4uS x 8 ( prescale ) x 16 + 8/10us ( int handling code )= (about) 60 us MOVWF TMR0 GOTO INTX ; INT_ABORT_RX BCF _RxCanStart ; clear flag ( forces a 'new' rx start delay ) GOTO INT_IDLE ; INT_CHECK_BUFF: ; check if we have any keys to send to pc MOVF KeyBufferTail,W ; get end of buffer to 'w' SUBWF KeyBufferHead,W ; subtract start of buffer if head - tail = zero, no byte BTFSC STATUS, Z ; zero flag = no byte to send GOTO INT_IDLE ; then do the 'idle' stuff INT_SEND_KEY ;key in buffer, get it and initiate an tx seq... CALL GET_KEY_BUFFER ; get the key into CurrKey MOVF CurrKey,W MOVWF LastKey ; store last sent key ; setup our tx/rx vars CLRF Comm_Flags ; used by both tx/rx routines ( and _RxCanStart bit !! ) CLRF TRX_Flags ; clear tx/rx flags CLRF KeyParity ; clear parity counter BSF _TX_Mode ; set tx mode flag.. ; preset bit and clock counters MOVLW H'2B' ; = 43 dec, will toggle clock out put every even number until zero MOVWF clkCount ; preset clock pulse counter MOVLW H'12' ; = 18 dec, will shift data out every even number until zero ; then parity bit will be set instead MOVWF bitCount ; preset bit counter ; data now set, initiate the clock to generate a 20 us tick ( rx/tx heart beat ) BSF pcDATA_out ; start bit, always 'low' ( we pull down ) MOVLW H'FA' ; start timer with 252 ( 256-6 = 250 ) ; timetick = 0.4uS x 8 ( prescale ) x 6 + 8 ( int handling code )= 27 us MOVWF TMR0 GOTO INTX ; exit, the next int will start an tx INT_IDLE: ; 'Idle' code i.e. no rx or tx possible AND/OR nothing to send DECF Divisor_10ms,F ; Count 0.5ms down to give 10 milli second tick BNZ INTX ; Exit if divider not zeroed MOVLW .20 MOVWF Divisor_10ms ; Preset the divide by 20 ;+++ ; 10 ms tick here ; Divide the 10 ms tick to give 100 ms second tick INT_10MS DECF Divisor_100ms,F ; Count 10ms down to give 100 milli second tick BNZ INTX ; Exit if divider not zeroed MOVLW .10 MOVWF Divisor_100ms ; Preset the divide by 10 ;+++ ; 100 ms tick here INT_100MS ; numlock check !! bit variable _Numlock does not actually set the numlock status directly. ; However, by setting this bit to '1' we make a test against the current numlock led status ; ( bit no 1 in variable KbLedStatus ), if that is different from this variable ; we send a 'numlock' press/release ( to toggle numlock status ) ; so in essence, setting this bit to '1' will force numlock to be 'on' etc. BTFSC _IsFirstLedStatus ; if = 1 then we have not yet received numlock status GOTO INT_REPEAT_CHECK ; nope, then this is a consecutive byte, store as 'normal' BTFSS _WaitNumLock ; are we waiting for pc numlock reply ? GOTO INT_NUMLOCK_CHECK ; yep then do repeat check instead DECFSZ Temp_Var,F ; GOTO INT_REPEAT_CHECK CALL RELEASE_NUMLOCK INT_NUMLOCK_CHECK BTFSC _LedNumLock ; is the led on ? GOTO INT_NUMLOCK_ON ; yep, then test our 'local' numlock state ( wanted numlock state ) ; nope numlock is off, is our wanted state also off ? BTFSS _NumLock ; is wanted state off ? GOTO INT_REPEAT_CHECK ; yep continue CALL PRESS_NUMLOCK ; nope then send numlock press/release code GOTO INT_REPEAT_CHECK INT_NUMLOCK_ON BTFSC _NumLock ; is wanted state also 'on' ? GOTO INT_REPEAT_CHECK ; yep CALL PRESS_NUMLOCK ; nope then toggle numlock state INT_REPEAT_CHECK ; check if a key should be 'repeated' ( when pressed longer than 500 ms ) BTFSS _startRepeat ; start repeating a key ? ( delay !!! ) GOTO INT_CHECK_KEY ; nope, then check if key should be repeated DECF RepeatTimer,F ; BNZ INT_500MS ; not zero yet, check timer instead BCF _startRepeat ; stop repeat timer ( delay is accomplished ) BSF _doRepeat ; and enable 'key' is still down check MOVLW .02 ; start repeat send timer MOVWF Divisor_Repeat ; GOTO INT_500MS ; do next timer check INT_CHECK_KEY BTFSS _doRepeat ; key should be repeated ? GOTO INT_500MS ; nope ; ok key should be repeated, check if it still pressed ? CALL CHECK_KEY_STATE ; uses MakeKeyOffset to calculate which key that was ; the last pressed, and then check if it's still pressed ; if still pressed carry = '1', BTFSS STATUS,C ; check carry BCF _doRepeat ; clear repeat bit, stop repeating the key BTFSS _doRepeat ; still pressed ? GOTO INT_500MS ; nope DECF Divisor_Repeat,F ; should we send the key ? BNZ INT_500MS ; nope MOVLW DELAY_RATE ; reload timer with key rate delay ;MOVLW .02 ; restart timer MOVWF Divisor_Repeat ; BSF _doSendKey ; set flag to send key, NOTE the actual sending ( putting into send buffer ) ; is done inside mainloop. INT_500MS DECF Divisor_500ms,F ; Count 100ms down to give 500 milli second tick BNZ INTX ; Exit if divider not zeroed MOVLW .05 MOVWF Divisor_500ms ; Preset the divide by 5 ;+++ ; 500 ms tick here INT_500_NEXT TOGGLE_PIN O_led_KEYCOMM_ok ; toggle the disco light ;-) BTFSS _DoExitAltKeymap ; is the alt keymap toggle key pressed the second time ? ; if so skip timeout test and exit BTFSS _InAltKeymap ; are we in altkeymap ? GOTO INTX ; nope ; we are in altkeymap, decrement the lastkeytime ; and check if we are at zero then we exit ; the altkeymap. DECF LastKeyTime,F ; decrease time BNZ INTX ; exit, timer has not expired ; timer expired, get out of altkey map BSF _ExitAltKeymap ; ; ***************** 'heart' beat code end *************** INTX ;BCF INTCON,T0IF ; Clear the calling flag PULL ; Restore registers RETFIE ; **************** end interrupt routine ************** ;+++++ ; Routines that will 'toggle' keyboard numlock status ; by sending numlock make/break code ; PRESS_NUMLOCK: MOVLW H'77' ; numlock key scancode, make CALL ADD_KEY MOVLW H'06' ; 6 x 100 ms = 600 ms ( release delay ) MOVWF Temp_Var ; BSF _WaitNumLock ; we are waitin for numlock status reply from pc RETURN RELEASE_NUMLOCK: MOVLW BREAK ; break prefix CALL ADD_KEY MOVLW H'77' ; numlock key scancode CALL ADD_KEY BCF _WaitNumLock RETURN ; *********************************************************************** ; ; CHECK_RX_KEY - handles the received commands from pc ; CHECK_RX_KEY ; check the key in 'currkey' ( command from pc ) CHECK_ED MOVF CurrKey,W ; move key buffer into W register SUBLW H'ED' ; subtract value in W with 0xED BTFSS STATUS, Z ; check if the zero bit is set GOTO CHECK_EE ; the result of the subtraction was not zero check next ; ok 'ED'=set status leds ( in next byte ) received BSF _IsLedStatus ; set bit that next incoming byte is kb led staus GOTO CHECK_SEND_ACK ; send ack CHECK_EE MOVF CurrKey,W ; move key buffer into W register SUBLW H'EE' ; subtract value in W with 0xEE BTFSS STATUS, Z ; check if the zero bit is set GOTO CHECK_F0 ; the result of the subtraction was not zero check next ; ok 'EE'= echo command received GOTO CHECK_SEND_EE ; send echo CHECK_F0 MOVF CurrKey,W ; move key buffer into W register SUBLW H'F0' ; subtract value in W with 0xF0 BTFSS STATUS, Z ; check if the zero bit is set GOTO CHECK_F2 ; the result of the subtraction was not zero check next ; ok 'F0'= scan code set ( in next commming byte ) received BSF _SkipByte ; skip next incomming byte ( or dont interpret ) GOTO CHECK_DONE ; do not send ack ! CHECK_F2 MOVF CurrKey,W ; move key buffer into W register SUBLW H'F2' ; subtract value in W with 0xF0 BTFSS STATUS, Z ; check if the zero bit is set GOTO CHECK_F3 ; the result of the subtraction was not zero check next ; ok 'F2'= Read ID command responds with 'AB' '83' GOTO CHECK_SEND_ID ; send id bytes CHECK_F3 MOVF CurrKey,W ; move key buffer into W register SUBLW H'F3' ; subtract value in W with 0xF3 BTFSS STATUS, Z ; check if the zero bit is set GOTO CHECK_FE ; GOTO CHECK_F4 ; the result of the subtraction was not zero check next ; ok 'F3'= set repeat rate ( in next commming byte ) received BSF _IsRateDelay ; next incomming byte is rate/delay info GOTO CHECK_SEND_ACK ; send ack ; **** Note ! removed from test as I 'don't care' ******** ; **** i.e. I dont disable or enable the keyboard at any time. ;CHECK_F4 ; MOVF CurrKey,W ; move key buffer into W register ; SUBLW H'F4' ; subtract value in W with 0xF4 ; BTFSS STATUS, Z ; check if the zero bit is set ; GOTO CHECK_F5 ; the result of the subtraction was not zero check next ; ok 'F4'= keyboard enable received ; GOTO CHECK_SEND_ACK ; send ack ;CHECK_F5 ; MOVF CurrKey,W ; move key buffer into W register ; SUBLW H'F5' ; subtract value in W with 0xF5 ; BTFSS STATUS, Z ; check if the zero bit is set ; GOTO CHECK_FE ; the result of the subtraction was not zero check next ; ok 'F5'= keyboard disable received ; GOTO CHECK_SEND_ACK ; send ack CHECK_FE MOVF CurrKey,W ; move key buffer into W register SUBLW H'FE' ; subtract value in W with 0xFE BTFSS STATUS, Z ; check if the zero bit is set GOTO CHECK_FF ; the result of the subtraction was not zero check next ; ok 'FE'= resend last sent byte MOVF LastKey,W ; get last key CALL ADD_KEY ; and put it on the que GOTO CHECK_DONE CHECK_FF MOVF CurrKey,W ; move key buffer into W register SUBLW H'FF' ; subtract value in W with 0xFF BTFSS STATUS, Z ; check if the zero bit is set GOTO CHECK_ERROR ; the result of the subtraction was not zero, unknown command ; ok 'FF'= reset keyboard received GOTO CHECK_SEND_AA ; send 'AA' power on self test passed CHECK_ERROR ; unknown command ( or command not interpreted ) GOTO CHECK_SEND_ACK CHECK_SEND_ID MOVLW H'FA' ; keyboard ack CALL ADD_KEY ; MOVLW H'AB' ; keyboard id first byte, always 0xAB CALL ADD_KEY ; MOVLW H'83' ; keyboard id second byte, always 0x83 CALL ADD_KEY ; GOTO CHECK_DONE CHECK_SEND_ACK MOVLW H'FA' ; keyboard ack CALL ADD_KEY ; GOTO CHECK_DONE CHECK_SEND_AA MOVLW H'FA' ; keyboard ack CALL ADD_KEY ; MOVLW H'AA' ; keyboard post passed CALL ADD_KEY ; GOTO CHECK_DONE CHECK_SEND_EE MOVLW H'EE' ; keyboard echo CALL ADD_KEY ; CHECK_DONE RETLW 0 ; and we are done ; *********************************************************************** ; Buffer code ( a bit modified ) from Stewe Lawther ; http://ourworld.compuserve.com/homepages/steve_lawther/ucindex.htm ; And of course source of the exellent keyboard viewer. !! ( without which, this ; project would have been close to impossible ) ( and of course my nifty ; memory oscilloscope ) ; ; ADD_KEY_BUFFER - (outside int)add the key in CurrKey to our keybuffer que in the first ; free position. If there is no more room the oldest byte is ; 'dumped'. ; ADD_KEY - Same but to be used inside int routine.( just skips int disable code ) ADD_KEY_BUFFER ADD_STOP_INT ; first stop all interrupts !!!!!!! BCF INTCON,GIE ; disable global interrupts.. BTFSC INTCON,GIE ; check that is really was disabled GOTO ADD_STOP_INT ; nope try again ADD_KEY ; inside interuppt we call this instead ( as we dont need to disable int :-) ) MOVWF BufTemp ; store key temporary MOVF KeyBufferHead,W ; move buffer head out of FSR temporarily MOVWF Temp ; store in temp MOVF KeyBufferTail,W ; set FSR to buffer tail MOVWF FSR ; set indirect file pointer MOVF BufTemp,W ; set W to new scancode to send MOVWF INDF ; and put it in the buffer MOVF Temp,W ; get the head pointer back MOVWF KeyBufferHead ; INCF KeyBufferTail,W ; get the end of the buffer SUBLW KbBufferMax ; check if at buffer max INCF KeyBufferTail,W ; (reload value to w - doesn't affect C) BTFSS STATUS, C ; if so (negative result) MOVLW KbBufferMin ; set to buffer min ( wrap around ) MOVWF KeyBufferTail SUBWF KeyBufferHead,W ; see if we have any room ( head and tail have meet ) BTFSC STATUS, Z ; if so (Z set) CALL INC_KEY_HEAD ; dump oldest byte ; finally turn on interrupts again MOVLW b'10100000' ; enable global & TMR0 interrupts MOVWF INTCON RETURN ; *********************************************************************** ; ; GET_KEY_BUFFER - Gets a char from the buffer, and puts it into KeyBuffer ; NOTE: Does not increase buffer pointers ( dump this key ). ; A call to INC_KEY_HEAD will do this if the key is sent ok GET_KEY_BUFFER MOVF INDF, W ;put the byte to send into key buffer MOVWF CurrKey RETURN ; and go back, NOTE ! the key is not ; removed from the buffer until a call ; to INC_KEY_HEAD is done. ; *********************************************************************** ; ; INC_KEY_HEAD - dump oldest byte in keybuffer, Do not call if byte ; has not been fetched before ( GET_KEY_BUFFER ) ; INC_KEY_HEAD INCF KeyBufferHead,W ; set to next byte in buffer SUBLW KbBufferMax ; check if at buffer max INCF KeyBufferHead,W ; (reload value to w - doesn't affect C) BTFSS STATUS, C ; if so (negative result) MOVLW KbBufferMin ; set to buffer min ( wrap around ) MOVWF KeyBufferHead ; and store ( in FSR ) RETURN ; go back ; *********************************************************************** ; ; CHECK_KEY_STATE - Check if the last pressed key is still pressed ; Returns with carry = '1' if still pressed ; else carry = '0' ( or error ) ; CHECK_KEY_STATE: ; uses LastMakeOffset to calculate which key to test MOVF LastMakeOffset,W ; get offset ANDLW H'18' ; mask out column bits ; lastmake offset has the following bits: ; '000yyxxx' where 'yy' is column no ; and 'xxx' is key num, BTFSC STATUS,Z ; zero = column 1 & 2 GOTO CHECK_COL_12 ; it is in column 1 MOVWF repTemp ; save it temporary SUBLW H'08' ; subtract value in W with 0x08 ( columns 3 & 4 ) BTFSC STATUS, Z ; check if the zero bit is set GOTO CHECK_COL_34 ; it is in column 3 & 4 MOVF repTemp,W ; get the column bits back SUBLW H'10' ; subtract value in W with 0x10 ( columns 5 & 6 ) BTFSC STATUS, Z ; check if the zero bit is set GOTO CHECK_COL_56 ; it is in column 5 & 6 CHECK_COL_78 MOVF kbColumn78_Old,W ; get bit map ( key status ) for keys in column 7 & 8 MOVWF repKeyMap ; and store it GOTO CHECK_KEY ; and continue to check bit CHECK_COL_56 MOVF kbColumn56_Old,W ; get bit map ( key status ) for keys in column 5 & 6 MOVWF repKeyMap ; and store it GOTO CHECK_KEY ; and continue to check bit CHECK_COL_34 MOVF kbColumn34_Old,W ; get bit map ( key status ) for keys in column 3 & 4 MOVWF repKeyMap ; and store it GOTO CHECK_KEY ; and continue to check bit CHECK_COL_12 MOVF kbColumn12_Old,W ; get bit map ( key status ) for keys in column 1 & 2 MOVWF repKeyMap ; and store it ;<-------Alt keymap code-------> ;Checks ONLY column 1&2 bitmap ( keymap ) ( as it is now ) ; this code has to be moved/changed if another column is choosen as the alt. keymap toggle key ; alternative keymap handling if key r3 c1 is pressed ( bit 2 in column 1&2 ) ; then enable alternative keymap ( only if keyrepeat is disabled ) ; check if this was the last key pressed ; check bit representing the alt. keymap key ( i've choosen key 2 ) MOVF LastMakeOffset,W ; get key offset again ANDLW H'07' ; mask out column bits SUBLW H'02' ; check if its bit num 2 ( the enter 'alt keymap' key ) BTFSS STATUS, Z ; check if the zero bit is set GOTO CHECK_KEY ; nope than another key was the last ; skip altkeymap enable ; the altkeymap key was the last pressed ! ; is key repeat disabled ? BTFSS I_jmp_NoRepeat ; check if repeat code is enabled ? GOTO CHECK_KEY ; yep, then skip altkeymap enable test ; enable altkeymap if key is still pressed BTFSC repKeyMap,2 ; test bit 2 ( should be key 'F7' ) GOTO CHECK_ENABLE_ALT ; ok enable altkeymap ( if we are not already in altkeymap ) GOTO CHECK_KEY ; nope another key in column 1&2 continue check CHECK_ENABLE_ALT BTFSC _AltKeymap ; are we already in altkeymap ? GOTO CHECK_KEY ; yep then just continue ; We are just entering/enabling the alt. keymap BSF _AltKeymap ; enable alternative keymap ; Example of using an 'advanced' alt keymap handling ; not enabled, to avoid intial confusion. ; I.E This snippet would only be called once when we ; are just entering(enabling) the alternative keymapping ! ; This example code will 'soft' release the current altkeymap key ( it is in pressed state ! ) ; ( i.e send release code for the enter alt. keymap key 'F7' ) ; send the make scancode for left <alt> key instead. ; and force numlock to be off. ; Do not use if you dont understand the implifications ! ; Also note that the scancodes are hardcoded here ! ; ( i.e do not use the lookup table definition of the key/s ) ; ***** start snippet ;MOVLW BREAK ; send break prefix ;CALL ADD_KEY ;MOVLW H'83' ; and scancode for the enter alt keymap ;CALL ADD_KEY ;MOVLW H'11' ; send make code for the left <alt> key ;CALL ADD_KEY ; example of forcing the numlock status to a particular state ; the numlockstatus will change ( be checked ) inside the int routine ; See also at the end of KB_DEBOUNCE_12 where the numlock status ; will be restored when we release the key ;BCF _NumLock ; 'force' numlock to be off ; This bit MUST also be checked as we do not know if we have recevied ; first numlock status byte yet ( pc does not send numlock/led status ; after intial poweron, if not one of the numlock/capslock/scrolllock are pressed ) ; i.e. if you connect this keyboard to a 'running' pc, the numlock status ; will be unknown.However if connected before poweron, it will be updated ; as numlock status is sent during pre-boot seq. ;BTFSC _IsFirstLedStatus ; have we recevied numlock status yet ? ;CALL PRESS_NUMLOCK ; ***** end snippet CHECK_KEY ; 'normal' key down check ; column for pressed key is now in repKeyMap MOVF LastMakeOffset,W ; get offset again ANDLW H'07' ; mask out key number ( lowest 3 bits ) BTFSC STATUS,Z ; bit num zero ? GOTO CHECK_KEY_DONE ; yep lowest bit, check and return MOVWF repTemp ; and store it CHECK_KEY_LOOP RRF repKeyMap,F ; rotate one step to right DECFSZ repTemp,F ; decrement bit counter GOTO CHECK_KEY_LOOP ; loop again CHECK_KEY_DONE ; ok the key to test should now be the lowest bit in repKeyMap CLRC ; clear carry BTFSC repKeyMap,0 ; check bit 0 BSF STATUS,C ; ok key is pressed set carry RETURN ; and we are done.. ; *********************************************************************** ; ; DELAY_1ms - Delay routine ! used when scanning our own keyboard ; Delay is between output of adress to 4051 and reading of inputs ; Increase to have a slower 'scan' rate or decrease to have a higher scan rate. ; DELAY_1ms MOVLW H'F0' ; wait 255 cycles MOVWF kbTemp ; this var is 'safe' to be used in side mainloop MOVLW H'03' MOVWF kbState DELAY_LOOP DECFSZ kbTemp,F ; decrement GOTO $-1 ; MOVLW H'F0' MOVWF kbTemp DECFSZ kbState,F GOTO DELAY_LOOP RETURN ;--------------------------------------------------------------------------- ; ; Initialisation ; ;--------------------------------------------------------------------------- INIT: ;+++ ; Set up the ports ; PORT A BANK1 MOVLW b'00000110' ; Set port data directions RA1,RA2 inputs RA0,RA3,RA4 outputs MOVWF TRISA ; PC keyboard connections ; PORT B ; Used for our own 3x8 matrix keyboard BANK1 MOVLW b'11111000' ; Set port data directions RB4-RB7 inputs rest outputs MOVWF TRISB ; Clear all registers on bank 0 ( memory ) BANK0 MOVLW H'0C' MOVWF FSR INITMEM CLRF 0 ; Clear a register pointed to be FSR INCF FSR,F CLRWDT ; clear watchdog MOVLW H'50' ; Test if at top of memory SUBWF FSR,W BNZ INITMEM ; Loop until all cleared ;+++ ; Initiate the keybuffer pointers INIT_BUFF: MOVLW KbBufferMin ; get adress of first buffer byte MOVWF KeyBufferHead ; store in FSR MOVWF KeyBufferTail ; and set last byte to the same ( no bytes in buffer ) ;+++ ; Preset the timer dividers MOVLW .20 MOVWF Divisor_10ms MOVLW .10 MOVWF Divisor_100ms MOVLW .05 MOVWF Divisor_500ms ;+++ ; Set up Timer 0. ; Set up TMR0 to generate a 0.5ms tick ; Pre scale of /8, post scale of /1 BANK1 MOVLW b'00000010' ; Initialisation of TMR0 prescale 8 '010' ; weak pullup enabled by latch values. MOVWF OPTION_REG ; load option reg with prescale of 8 BANK0 MOVLW H'64' ; start timer with 100 ( 256-100 = 156 ) ; timetick = 0.4uS x 8 ( prescale ) x 156 = (roufly) 0.5 mS MOVWF TMR0 ;--------------------------------------------------------------------------- ; ; the main 'program' loop ( starts really at MAIN_LOOP ) ; ;--------------------------------------------------------------------------- MAIN: BSF pcDATA_in BSF pcCLOCK_in BCF pcDATA_out BCF pcCLOCK_out CLRF PORTB MOVLW H'08' ; preset the column counter MOVWF kbColumnCnt ; BSF _NumLock ; default state is numlock = on BSF _IsFirstLedStatus ; we have not yet recevied led status byte. MOVLW b'10100000' ; enable global & TMR0 interrupts MOVWF INTCON CLRWDT ; clear watchdog BTFSS O_led_KEYCOMM_ok GOTO $-2 ; make an 0.5 second delay here ; i.e. the led will come on when 0.5 seconds has passed ; set inside the timer int. CLRWDT ; clear watchdog BTFSC O_led_KEYCOMM_ok GOTO $-2 ; make an additional 0.5 second delay here ; i.e. the led will be dark when 0.5 seconds has passed ; set inside the timer int. MOVLW H'AA' ; post passed :-), always 0xAA CALL ADD_KEY_BUFFER ; now go into infinite loop, the pc kb interface runs in the background ( as an int ) ; where we continuously monitor the pcCLOCK/DATA_in lines MAIN_LOOP: ; check whatever :-) CLRWDT ; clear watchdog MAIN_CHECK_COL_1: ; scan our own keyboard, first four bits ; address and read column, read as complement so key pressed = '1' ; since we pull down when key is pressed ( weak pullup enabled ) ; ( i.e. pressed key has level 0V ) to make it more 'logical' to work with CLRF kbColumnVal ; get column counter / adress out MOVF kbColumnCnt,W MOVWF PORTB ; set the columns adress to the 74HCT4051 ; i.e. make column low IFNDEF DEBUG CALL DELAY_1ms ; wait 1 ms let pins stabilize ENDIF COMF PORTB,W ; read back the pin values ( complement i.e. key pressed = '1' ) ANDLW b'11110000' ; mask out unused pins MOVWF kbColumnVal ; store the pin values SWAPF kbColumnVal,F ; swap nibbles ( low<->high ) to make room for next column INCF kbColumnCnt,F ; inc column adress MAIN_CHECK_COL_2: ; read next four bits ; put out adress and read next column, read as complement so key pressed = '1' ; this as we pull down when key is pressed ( weak pullup enabled ) ; get column counter / adress out MOVF kbColumnCnt,W MOVWF PORTB ; set the columns adress to the 74HCT4051 ; i.e. make column low IFNDEF DEBUG CALL DELAY_1ms ; wait 1 ms ENDIF COMF PORTB,W ; read back the pin values ( complement i.e. key pressed = '1' ) ANDLW b'11110000' ; mask out unused pins ADDWF kbColumnVal,F ; and store pin values INCF kbColumnCnt,F ; reset column counter check ; i.e. we are 'only' using adress 0 - 7 MOVF kbColumnCnt,W SUBLW H'08' ; subtract value in W with 0x08 BTFSS STATUS, Z ; check if the zero bit is set GOTO MAIN_CHECK_DEBOUNCE ; nope continue CLRF kbColumnCnt ; reset counter/adress MAIN_CHECK_DEBOUNCE: CALL KB_DEBOUNCE ; do debouncing on the current values and send make/break ; for any key that has changed ; NOTE uses the current column adress to determine which ; columns to debounce! MAIN_REPEAT: BTFSS I_jmp_NoRepeat ; check if repeat code is enabled ? GOTO MAIN_CHECK_REPEAT ; yep check key repeating ; keyrepeat disabled then do check on exit of altkeymap instead BTFSS _ExitAltKeymap ; we want to exit altkeymap ? GOTO MAIN_LOOP ; nope ; check that ALL keys are released ; before exiting the alt keymap MOVF kbColumn78_Old,F ; reload column 78 to itself ( affect zero flag ) BTFSS STATUS,Z ; check if zero ? GOTO MAIN_LOOP ; key/s still down in column 78 MOVF kbColumn56_Old,F ; reload column 56 to itself ( affect zero flag ) BTFSS STATUS,Z ; check if zero ? GOTO MAIN_LOOP ; key/s still down in column 56 MOVF kbColumn34_Old,F ; reload column 34 to itself ( affect zero flag ) BTFSS STATUS,Z ; check if zero ? GOTO MAIN_LOOP ; key/s still down in column 34 MOVF kbColumn12_Old,F ; reload column 12 to itself ( affect zero flag ) BTFSS STATUS,Z ; check if zero ? GOTO MAIN_LOOP ; key/s still down in column 12 ; all keys released !! BCF _AltKeymap ; exit altkeymap BCF _ExitAltKeymap ; exit release check BCF _InAltKeymap ; clear flag for second keypress check BCF _DoExitAltKeymap ; GOTO MAIN_LOOP MAIN_CHECK_REPEAT BTFSS _doSendKey ; if we should send a repeated key GOTO MAIN_LOOP ; nope continue ; send the key in RepeatedKey but first check if its an extended key BTFSS _RepeatIsExt ; is it extended ? GOTO MAIN_SEND_REPEAT ; nope just send scan code ; last key pressed was extended send extended prefix MOVLW EXTENDED ; get extended code CALL ADD_KEY_BUFFER ; and put it into the buffer MAIN_SEND_REPEAT: MOVF RepeatKey,W ; get key code for the last pressed key CALL ADD_KEY_BUFFER ; and put it into the buffer BCF _doSendKey ; and clear the flag, it will be set again ; inside int handler if key still is pressed GOTO MAIN_LOOP ; and return ; *********************************************************************** ; ; KB_SEND_KEY - uses the Offset stored in var 'Offset' to fetch a key from our ; key lookup table. ; then checks the bit var _isBreak to see if make or break codes should be sent ; It then puts the code/s into the key buffer ( for sending later, in int routine ) KB_SEND_KEY: MOVF Offset,W ; get current offset MOVWF TempOffset ; save it ( to be used in key repeat code, is its 'make' ) ; temp offset has the following bits: ; '000yyxxx' where 'yy' is column offset ; and 'xxx' is key num, CLRC ; clear carry so it dont affect byte rotation RLF Offset,F ; first rotate RLF Offset,F ; second rotate ; offset no have the following bits: ; '0yyxxx01' where 'yy' is column offset ; and 'xxx' is key num, ; as each key in table has 4 bytes of 'space' INCF Offset,F ; add one, for the 'movwf pcl' at the start of the table BCF Offset,7 ; clear to bit, just in case so we dont ; 'overflow' the table, should not be needed ! BCF _isExtended ; clear extended flag MOVLW LOW LOOKUP_KEY ; get low bit of table adress ADDWF Offset,F ; 8 bit add MOVLW HIGH LOOKUP_KEY ; get high 5 bits BTFSC STATUS,C ; is page boundary crossed ? ADDLW 1 ; yep, then inc high adress MOVWF PCLATH ; load high adress in latch MOVF Offset,W ; load computed offset in w CLRC ; clear carry ( default= key is not extended ) ; if key is extended then carry is set in jumptable lookup_key CALL LOOKUP_KEY ; get key scan code/s for this key ; key scan code/s are saved in ; W - scancode, should go into kbScan ; carry set - extend code ; carry clear - not extended code MOVWF kbScan ; store scancode ; if carry is set then key is extended so first send extended code ; before any make or break code BTFSS STATUS,C ; check carry flag GOTO KB_CHK_BREAK ; nope then check make/break status BSF _isExtended ; set extended flag MOVLW EXTENDED ; CALL ADD_KEY_BUFFER ; get extended code and put in in the buffer KB_CHK_BREAK: ; check if it's make or break BTFSS _isBreak ; check if its pressed or released ? GOTO KB_DO_MAKE_ONLY ; send make code BCF _isBreak ; clear bit for next key ; break code, key is released MOVLW BREAK ; get break code CALL ADD_KEY_BUFFER ; and put into buffer GOTO KB_DO_MAKE ; and send key code also ; key is pressed ! KB_DO_MAKE_ONLY: BCF _doSendKey ; stop repeat sending BCF _doRepeat ; and bit for repeat key send BSF _startRepeat ; and set flag for start key repeat check BCF _RepeatIsExt ; clear repeat key extended flag ( just in case ) BTFSC _isExtended ; is it extended ? BSF _RepeatIsExt ; set the flag ; save this key in 'last' pressed, to be used in key repeat code MOVF TempOffset,W ; get saved offset MOVWF LastMakeOffset ; and store it ; if keyrepat = enabled, alternative mapping = disabled BTFSS I_jmp_NoRepeat ; check if repeat code is enabled ? GOTO KB_REP_NOR ; yep set normal delay ( 800 ms ) ; else keyrepat = disabled, alternative mapping = enabled MOVLW DELAY_ENTER_ALTKEYMAP ;reload delay before entering the altkeymap ( 3 sec ) ; i.e how long the enter altkeymap key must be pressed before ; we enable altkey keymap codes. GOTO KB_REP_SET ; and set it KB_REP_NOR: MOVLW DELAY_REPEAT ; reload 'normal' repeat delay ( 800 ms ) KB_REP_SET: MOVWF RepeatTimer ; and (re)start the timer for key repeat MOVF kbScan,W ; get key scan code MOVWF RepeatKey ; and save it KB_DO_MAKE: ; key pressed/released ( i.e. the scancode is sent both on make and break ) MOVF kbScan,W ; get scan code into w CALL ADD_KEY_BUFFER ; and add to send buffer ; reset the 'get out of alt. keymap timer for each keypress ; note don't care if we are 'in' alt. keymap. Reset this timer anyway ; as the code for checking if we are currently in alt. key map ; would be as long as it takes to reset the timer. MOVLW DELAY_EXIT_ALTKEYMAP ; reload the delay for exiting the altkeymap when no ; key is pressed ( 7.5 sec ) MOVWF LastKeyTime ; (re)set lastkey timer ( used to get out of altkeymap ) RETURN ; *********************************************************************** ; ; KB_DEBOUNCE - debounces two column readings from our keyboard ; If a bit 'state' has been 'stable' for 4 consecutive debounces ; the 'new' byte is updated with the new state ; 'normal' loop time ( no tx/rx/key press ) is about 2-3 ms ; so from 'key' down until 'new' is updated it takes about 8-10 ms ; ( as we are scanning columns two by two, the whole keyboard needs ; 4 loops to be fully updated, then 4 debounce samples for each 'pair' ) KB_DEBOUNCE: ; debounce current column(s) MOVF kbColumnCnt,F ; reload value into itself ( affect zero flag ) BTFSC STATUS,Z ; is it zero ? GOTO KB_DEBOUNCE_78 ; debounce columns 7 & 8 MOVF kbColumnCnt,W ; move column counter into W register SUBLW H'04' ; subtract value in W with 0x04 ( columns 5 & 6 ) BTFSC STATUS, Z ; check if the zero bit is set GOTO KB_DEBOUNCE_34 ; debounce columns 3 & 4 MOVF kbColumnCnt,W ; move column counter into W register SUBLW H'06' ; subtract value in W with 0x02 ( columns 3 & 4 ) BTFSC STATUS, Z ; check if the zero bit is set GOTO KB_DEBOUNCE_56 ; ok column 1 & 2 debounce ; all above tests 'failed' ; columns to debouce are 1 & 2 KB_DEBOUNCE_12: ; debounce columns 1 & 2 DEBOUNCE_BYTE kbColumnVal,kbColumn12_New,kbColumn12Cnt,kbColumn12State MOVF kbColumn12_New,W ; get debounced sample XORWF kbColumn12_Old,W ; get changed bits BTFSC STATUS,Z ; check if zero = no change RETURN ; no change. return ; key/s has been changed, w contains which key/s that has been changed in column 7 & 8 ; Note ! Not the actual state of the key, only 'change has occured' = '1' MOVWF kbState ; save change bit/s MOVLW H'07' ; preset bit counter MOVWF kbBitCnt ; loop though all eight bits. BCF _LastColumn ; clear end seq bit ( set when are done with last bit ) MOVF kbColumn12_New,W ; get new sample MOVWF kbTemp ; and store it KB_LOOP_12 CLRF Offset ; clear offset counter ( for table read ) CLRC ; clear carry RLF kbState,F ; rotate left, and store back BTFSS STATUS,C ; check carry '1' = bit was high = change has occured GOTO KB_LOOP_12_SKIP ; nope, no change check next bit ( or exit ) ; bit changed MOVF kbBitCnt,W ; get bit counter ( for offset calc. ) MOVWF Offset ; store bit num ( for offset ) CLRC ; clear carry RLF kbTemp,F ; rotate left ( next bit ) BTFSS STATUS,C ; check carry '1' = key is down ( i.e. make ) BSF _isBreak ; c = '0' = send break code, i.e. key is released CALL KB_SEND_KEY ; send key code/s make/break uses ; Offset, and _isBreak vars GOTO KB_LOOP_12_NEXT KB_LOOP_12_SKIP RLF kbTemp,F ; rotate so we read next key KB_LOOP_12_NEXT BTFSC _LastColumn ; are we done ? GOTO KB_12_DONE ; yep, save new key bit map and exit DECFSZ kbBitCnt,F ; decrement bit counter GOTO KB_LOOP_12 ; bits left BSF _LastColumn ; set bit so we break out after next run GOTO KB_LOOP_12 KB_12_DONE: ; and update our 'last known' status for the columns MOVF kbColumn12_New,W ; get new status MOVWF kbColumn12_Old ; and store it.. ;<-------Alt keymap code-------> ; ***** alternative keymap handling ; The alternative keymap is enabled by pressing key r4 c1 ( i.e. bit 3 in column 12 ) ; Here, we enable a check to turn off alternative keymap if ; that key and all others are released ( bit is cleared ). ; ( else no (alternative)break codes would be sent for those keys that are still pressed ) ; NOTE: _Altkeymap is set inside int routine when checking ; keyrepeat so there is a 'variable' delay before the altkeymap is active ; BTFSS _AltKeymap ; is altkeymap enabled ? RETURN ; nope return BTFSC _InAltKeymap ; are we in altkeymap ? GOTO KB_12_IN ; yep alt keymap key has been released once ; nope still waiting for first release BTFSS kbColumn12_Old,2 ; is key released ? ( first time ) GOTO KB_12_ALT ; yep, reset timers and set bit variables KB_12_IN BTFSC _DoExitAltKeymap ; are we waiting for release ? GOTO KB_12_OUT ; yes ; the key has been released once test for second press BTFSC kbColumn12_Old,2 ; is it still pressed ? GOTO KB_12_ALT2 ; yep BTFSS _DoExitAltKeymap ; are we now waiting for the last ( second ) release ? RETURN ; nope KB_12_OUT BTFSS kbColumn12_Old,2 ; check if key still pressed ? BSF _ExitAltKeymap ; nope, then enable exit check that ; will exit alt keymap as soon as all key are released KB_12_ALT2 BSF _DoExitAltKeymap ; check for second release RETURN KB_12_ALT ; first release of the enter alt keymap key ; reset 'get out' timer and set bit variables to enable check ; for second press/release MOVLW H'0F' ; x0.5 sec = 7.5 sec MOVWF LastKeyTime ; (re)set lastkey timer ( used to get out of altkeymap automaticly) BSF _InAltKeymap ; yep the first time, then set flag that we are now ; waiting for a second press/release to exit alt key map ; all keys are released before exiting altkeymap ;***** Example snippet(one line) to be paired with code in CHECK_KEY_STATE where I ; forced numlock status to be off while enetering the alt keymap ; but have not yet released the alt keymap toggle key. ; this code will be called at the first release of this key. Used ; to restore numlock status. ; As said before, do not use if implifications are not known ! ;BSF _NumLock ; and also force numlock to be 'on' ; as it is set to 'off' when we enter altkeymap ; we must set it 'back' RETURN KB_DEBOUNCE_34: ; debounce columns 3 & 4 DEBOUNCE_BYTE kbColumnVal,kbColumn34_New,kbColumn34Cnt,kbColumn34State MOVF kbColumn34_New,W ; get debounced sample XORWF kbColumn34_Old,W ; get changed bits BTFSC STATUS,Z ; check if zero = no change RETURN ; no change. return ; key/s has been changed, w contains which key/s that has been changed in column 7 & 8 ; Note ! Not the actual state of the key, only 'change has occured' = '1' MOVWF kbState ; save change bit/s MOVLW H'07' ; preset bit counter MOVWF kbBitCnt ; loop though all eight bits. BCF _LastColumn ; clear end seq bit ( set when are done with last bit ) MOVF kbColumn34_New,W ; get new sample MOVWF kbTemp ; and store it KB_LOOP_34 CLRF Offset ; clear offset counter ( for table read ) CLRC ; clear carry RLF kbState,F ; rotate left, and store back BTFSS STATUS,C ; check carry '1' = bit was high = change has occured GOTO KB_LOOP_34_SKIP ; nope, no change check next bit ( or exit ) ; bit changed MOVF kbBitCnt,W ; get bit counter ( for offset calc. ) MOVWF Offset ; store bit num ( for offset ) BSF Offset,3 ; set bit 3 for table read ( column 3 & 4 ) ;BCF _isBreak ; clear break flag CLRC ; clear carry RLF kbTemp,F ; rotate left ( next bit ) BTFSS STATUS,C ; check carry '1' = key is down ( i.e. make ) BSF _isBreak ; c = '0' = send break code, i.e. key is released CALL KB_SEND_KEY ; send key code/s make/break uses ; Offset, and _isBreak vars GOTO KB_LOOP_34_NEXT KB_LOOP_34_SKIP RLF kbTemp,F ; rotate so we read next key KB_LOOP_34_NEXT BTFSC _LastColumn ; are we done ? GOTO KB_34_DONE ; yep, save new key bit map and exit DECFSZ kbBitCnt,F ; decrement bit counter GOTO KB_LOOP_34 ; bits left BSF _LastColumn ; set bit so we break out after next run GOTO KB_LOOP_34 KB_34_DONE: ; and update our 'last known' status for the columns MOVF kbColumn34_New,W ; get new status MOVWF kbColumn34_Old ; and store it.. RETURN KB_DEBOUNCE_56: ; debounce columns 5 & 6 DEBOUNCE_BYTE kbColumnVal,kbColumn56_New,kbColumn56Cnt,kbColumn56State MOVF kbColumn56_New,W ; get debounced sample XORWF kbColumn56_Old,W ; get changed bits BTFSC STATUS,Z ; check if zero = no change RETURN ; no change. return ; key/s has been changed, w contains which key/s that has been changed in column 7 & 8 ; Note ! Not the actual state of the key, only that 'a change has occured' = '1' MOVWF kbState ; save change bit/s MOVLW H'07' ; preset bit counter MOVWF kbBitCnt ; loop though all eight bits. BCF _LastColumn ; clear end seq bit ( set when are done with last bit ) MOVF kbColumn56_New,W ; get new sample MOVWF kbTemp ; and store it KB_LOOP_56 CLRF Offset ; clear offset counter ( for table read ) CLRC ; clear carry RLF kbState,F ; rotate left, and store back BTFSS STATUS,C ; check carry '1' = bit was high = change has occured GOTO KB_LOOP_56_SKIP ; nope, no change check next bit ( or exit ) ; bit changed MOVF kbBitCnt,W ; get bit counter ( for offset calc. ) MOVWF Offset ; store bit num ( for offset ) BSF Offset,4 ; set bit 4 for table read ( column 5 & 6 ) CLRC ; clear carry RLF kbTemp,F ; rotate left ( next bit ) BTFSS STATUS,C ; check carry '1' = key is down ( i.e. make ) BSF _isBreak ; c = '0' = send break code, i.e. key is released CALL KB_SEND_KEY ; send key code/s make/break uses ; Offset, and _isBreak vars GOTO KB_LOOP_56_NEXT KB_LOOP_56_SKIP RLF kbTemp,F ; rotate so we read next key KB_LOOP_56_NEXT BTFSC _LastColumn ; are we done ? GOTO KB_56_DONE ; yep, save new key bit map and exit DECFSZ kbBitCnt,F ; decrement bit counter GOTO KB_LOOP_56 ; bits left BSF _LastColumn ; set bit so we break out after next run GOTO KB_LOOP_56 KB_56_DONE: ; and update our 'last known' status for the columns MOVF kbColumn56_New,W ; get new status MOVWF kbColumn56_Old ; and store it.. RETURN KB_DEBOUNCE_78: ; debounce columns 7 & 8 DEBOUNCE_BYTE kbColumnVal,kbColumn78_New,kbColumn78Cnt,kbColumn78State MOVF kbColumn78_New,W ; get debounced sample XORWF kbColumn78_Old,W ; get changed bits BTFSC STATUS,Z ; check if zero = no change RETURN ; no change. return ; key/s has been changed, w contains which key/s that has been changed in column 7 & 8 ; Note ! Not the actual state of the key, only 'change has occured' = '1' MOVWF kbState ; save change bit/s MOVLW H'07' ; preset bit counter MOVWF kbBitCnt ; loop though all eight bits. ( 7-0 ) BCF _LastColumn ; clear end seq bit ( set when are done with last bit ) MOVF kbColumn78_New,W ; get new sample MOVWF kbTemp ; and store it KB_LOOP_78 CLRF Offset ; clear offset counter ( for table read ) CLRC ; clear carry RLF kbState,F ; rotate left, and store back BTFSS STATUS,C ; check carry '1' = bit was high = change has occured GOTO KB_LOOP_78_SKIP ; nope, no change check next bit ( or exit ) ; bit changed MOVF kbBitCnt,W ; get bit counter ( for offset calc. ) MOVWF Offset ; store bit num ( for offset ) BSF Offset,4 ; set bit 3,4 for table read ( column 7 & 8 ) BSF Offset,3 ; CLRC ; clear carry RLF kbTemp,F ; rotate left ( next bit ) BTFSS STATUS,C ; check carry '1' = key is down ( i.e. make ) BSF _isBreak ; c = '0' = send break code, i.e. key is released CALL KB_SEND_KEY ; send key code/s make/break uses ; Offset, and _isBreak vars GOTO KB_LOOP_78_NEXT KB_LOOP_78_SKIP RLF kbTemp,F ; rotate so we read next key KB_LOOP_78_NEXT BTFSC _LastColumn ; are we done ? GOTO KB_78_DONE ; yep, save new key bit map and exit DECFSZ kbBitCnt,F ; decrement bit counter GOTO KB_LOOP_78 ; bits left BSF _LastColumn ; set bit so we break out after next run GOTO KB_LOOP_78 KB_78_DONE: ; and update our 'last known' status for the columns MOVF kbColumn78_New,W ; get new status MOVWF kbColumn78_Old ; and store it.. RETURN ; *********************************************************************** ; ; LOOKUP_KEY - lookup table for key scancodes. ; Returns a scancode in w ; Sets carry if key is extended ; NOTE: If key R3 C1 has been pressed longer than keyrepeat delay ; AND keyrepeat is disabled ( jumper on pin RB3 ) THEN the ; bit _AltKeymap is set and we can return an alternative scancode in W ; LOOKUP_KEY ; lookup table for the keys ( 32 keys x 4 lines + 1 = 129 lines ) ; keys are labelled Rx - Cy where x = row number and y = column number ; handles a 4 row x 8 column keyboard = 32 keys MOVWF PCL ; add to program counter ; R1 - C1 i.e. key 1 NOP NOP NOP RETLW H'05' ; scan code 'F1' ; R2 - C1 i.e. key 2 NOP NOP NOP RETLW H'0C' ; scan code 'F4' ; R3 - C1 i.e. key 3 ; The famous alternative keymap toggle key !!! ;-) ; It is adviced that this key does not use an alt scancode ; makes things cleaner and simplified. ; IF USED though, remember that a 'soft' release code must be sent ; for this key when entering the altkeymap !( + 'soft' make code for the alternative key ) ; This as the key is pressed when entering altkeymap ; which makes the bit _Altkeymap be set, and hence when released ; the release code for this 'normal' key will never be sent ; instead the release code for the alternative key will be sent. ; To 'fix' this put the release code at the end of CHECK_KEY_STATE ( where the ; alt keymap bit is set ).i.e. break prefix+scancode for normal key. NOP NOP NOP RETLW H'83' ; scan code for F7 ( in 'normal' mode ) ; R4 - C1 i.e. key 4 BTFSC _AltKeymap ; check for alternative keymap RETLW H'76' ; send scancode for 'ESC' instead BSF STATUS,C ; set carry ( i.e. extended code ) RETLW H'6B' ; scan code 'arrow left' '<-' ; R1 - C2 i.e. key 5 NOP NOP NOP RETLW H'06' ; scan code 'F2' hex06 ; R2 - C2 i.e. key 6 NOP NOP NOP RETLW H'03' ; scan code 'F5' ; R3 - C2 i.e. key 7 BTFSC _AltKeymap ; check for alternative keymap RETLW H'0D' ; send scancode for 'horizontaltab' HT instead BSF STATUS,C ; set carry ( i.e. extended code ) RETLW H'75' ; scan code 'arrow up' '^' ; R4 - C2 i.e. key 8 BTFSC _AltKeymap ; check for alternative keymap RETLW H'14' ; send scancode for 'left ctrl' instead BSF STATUS,C ; set carry ( i.e. extended code ) RETLW H'72' ; scan code 'arrow down' ; R1 - C3 i.e. key 9 NOP NOP NOP RETLW H'04' ; scan code 'F3' ; R2 - C3 i.e. key 10 NOP NOP NOP RETLW H'0B' ; scan code 'F6' ; R3 - C3 i.e. key 11 NOP NOP NOP RETLW H'0A' ; scan code 'F8' ; R4 - C3 i.e. key 12 BTFSC _AltKeymap ; check for alternative keymap RETLW H'11' ; send scancode for 'left alt' instead BSF STATUS,C ; set carry ( i.e. extended code ) RETLW H'74' ; scan code 'arrow right' '->' ; R1 - C4 i.e. key 13 BTFSC _AltKeymap ; check for alternative keymap RETLW H'6C' ; send scancode for numeric '7' instead NOP RETLW H'3D' ; scan code '7' ; R2 - C4 i.e. key 14 BTFSC _AltKeymap ; check for alternative keymap RETLW H'6B' ; send scancode for numeric '4' instead NOP RETLW H'25' ; scan code '4' ; R3 - C4 i.e. key 15 BTFSC _AltKeymap ; check for alternative keymap RETLW H'69' ; send scancode for numeric '1' instead NOP RETLW H'16' ; scan code '1' ; R4 - C4 i.e. key 16 BTFSC _AltKeymap ; check for alternative keymap RETLW H'7B' ; send scancode for numeric '-' instead NOP RETLW H'4A' ; scan code '-' minus ( swe kbd ) ; R1 - C5 i.e. key 17 BTFSC _AltKeymap ; check for alternative keymap RETLW H'75' ; send scancode for numeric '8' instead NOP RETLW H'3E' ; scan code '8' ; R2 - C5 i.e. key 18 BTFSC _AltKeymap ; check for alternative keymap RETLW H'73' ; send scancode for numeric '5' instead NOP RETLW H'2E' ; scan code '5' ; R3 - C5 i.e. key 19 BTFSC _AltKeymap ; check for alternative keymap RETLW H'72' ; send scancode for numeric '2' instead NOP RETLW H'1E' ; scan code '2' ; R4 - C5 i.e. key 20 BTFSS _AltKeymap ; check for alternative keymap RETLW H'45' ; scan code '0' ( from keypad ) normal key BSF STATUS,C ; set carry ( i.e. extended code ) RETLW H'1F' ; alt keycode ( windows start menu activate ) ; R1 - C6 i.e. key 21 BTFSC _AltKeymap ; check for alternative keymap RETLW H'7D' ; send scancode for numeric '9' instead NOP RETLW H'46' ; scan code '9' ; R2 - C6 i.e. key 22 BTFSC _AltKeymap ; check for alternative keymap RETLW H'74' ; send scancode for numeric '6' instead NOP RETLW H'36' ; scan code '6' ; R3 - C6 i.e. key 23 BTFSC _AltKeymap ; check for alternative keymap RETLW H'7A' ; send scancode for numeric '3' instead NOP RETLW H'26' ; scan code '3' ; R4 - C6 i.e. key 24 BTFSS _AltKeymap ; check for alternative keymap RETLW H'49' ; scan code '.' ( swe kbd ) normal key ; use alternative keymap BSF STATUS,C ; set carry ( i.e. extended code ) RETLW H'4A' ; send scancode for numeric '/' instead ; R1 - C7 i.e. key 25 BTFSC _AltKeymap ; check for alternative keymap RETLW H'79' ; send scancode for numeric '+' instead NOP RETLW H'4E' ; scan code '+' ; R2 - C7 i.e. key 26 BTFSS _AltKeymap ; check for alternative keymap RETLW H'66' ; scan code 'back space' BS, normal key ; use alternative keymap BSF STATUS,C ; set carry ( i.e. extended code ) RETLW H'71' ; send scancode for 'delete' instead ; R3 - C7 i.e. key 27 BTFSS _AltKeymap ; check for alternative keymap RETLW H'5A' ; scan code 'enter', normal key ; use alternative keymap BSF STATUS,C ; set carry ( i.e. extended code ) RETLW H'5A' ; send scancode for numeric enter instead ( note ! extended ) ; R4 - C7 i.e. key 28 BTFSS _AltKeymap ; check for alternative keymap RETLW H'5A' ; scan code 'enter', normal key ; use alternative keymap BSF STATUS,C ; set carry ( i.e. extended code ) RETLW H'5A' ; send scancode for numeric enter instead ( note ! extended ) ; R1 - C8 i.e. key 29 NOP NOP NOP RETLW H'2C' ; scan code 't' ; R2 - C8 i.e. key 30 NOP NOP NOP RETLW H'24' ; scan code 'e' ; R3 - C8 i.e. key 31 NOP NOP NOP RETLW H'1B' ; scan code 's' ; R4 - C8 i.e. key 32 NOP NOP NOP RETLW H'2C' ; scan code 't' END Será que algum programador pode me ajudar? minha duvida é a seguinte ...eu encontrei um projeto na internet ae eu copiei o codigo mas quando eu coloco o codigo no mplab eu não consigo fazer o make ele da erro!! build falhou!! será que alguem pode me ajudar?? será que esse codigo ta errado!! se algum programador poder me ajudar eu agradeço!!! ; ; PC-Keyboard emulator using a PIC16F84 ; Scan a 32 key keyboard ( with alternative mapping = 64 keys ) ; ; COPYRIGHT (c)1999 BY Tony K&uuml;bek ; This is kindly donated to the PIC community. It may be used freely, ; and you are forbidden to deprive others from those rights. ; Please read the FSF (Free Software Foundation) GNU statement. ; ;********************************************************************* ; ; E-MAIL tony.kubek@flintab.se ; ; ; DATE 2000-01-23 ; ITERATION 1.0B ; FILE SAVED AS PiCBoard.ASM ; FOR PIC16F84-10/P ; CLOCK 10.00 MHz RESONATOR ( OSC-HS ) ; INSTRUCTION CLOCK 2.50 MHz T= 0.4 us ; SETTINGS WDT-ON, PWT-ON, CP-OFF ; REVISION HISTORY ; 0.1b - First beta, mainly for testing ; 1.0b - First public beta release ; ; ; ; ;*************************************************************************** ; ; PREFACE ; ; This is NOT an tutorial on pc keyboards in general, there are quite ; a few sites/etc that have that already covered. However i DID find ; some minor ambiguities regarding the actual protocol used but nothing ; that warrants me to rewrite an complete pc keyboard FAQ. ; So for 'general' pc keyboard info ( scancodes/protocol/pin config/etc ) ; here are some useful links: ; ; http://www.senet.com.au/~cpeacock/ ; http://ourworld.compuserve.com/homepages/steve_lawther/keybinfo.htm ; http://204.210.50.240/techref/default.asp?url=io/keyboard.htm ; http://www.arne.si/~mauricio/PIC.HTM ; ; PLEASE do not complain about code implementation, I know there are parts ; in which is it 'a bit' messy and hard to follow, and other parts where ; the code is not optimised. Take it as is. Futhermore I did hesitate ; to include all of the functionality thats currently in, but decided to ; keep most of it, this as the major complaint i had with the other available ; pc-keyboard code was just that - 'is was incomplete'. Also do not ; be discoraged by the size and complexity of it if you are an beginner, ; as a matter of fact this is only my SECOND program ever using a pic. ; I think I managed to give credit were credit was due ( 'borrowed code' ). ; But the originators of these snippets has nothing to do with this project ; and are probably totally unaware of this, so please do not contact them ; if you have problems with 'my' implementation. ; ; BTW ( shameless plug ) UltraEdit rules ! ( if you dont have it, get it ! ) ; http://www.ultraedit.com ( dont 'forget' the 'wordfile' for PIC, color indenting ) ; Without that I guess this file would be 'messy'. ; ; Ok with that out of the way here we go: ; ; ;*************************************************************************** ; DESCRIPTION ( short version ) ; A set of routines which forms a PC keyboard emulator. ; Routines included are: ; Interrupt controlled clock ( used for kb comm. and 'heart beat ) ; A 4x8 matrix keyboard scanner ( using an 74HCT4051 3 to 8 analogue multiplexer ) ; Communincation with PC keyboard controller, both send end recive. ; PC keyboard routines are using 4 pins (2 inputs & 2 outputs) to control ; keyboard's 2 bidirectional OC lines (CLK & DATA). The following ; 'drawing' conceptually shows how to connect the related pins/lines ; ; ( ASCII art/info shamelessly 'borrowed' from http://www.arne.si/~mauricio/PIC.HTM ) ; ; vcc vcc ; | | ; \ -+- ; / 2K2 /_\ 1N4148 ; \ | ; pcCLOCK_in -----------------o---o------o--------- kbd CLOCK ; | | | ; 2N2222 |50pF -+- ; pcCLOCK_out ____/\/\/\____|/ === /_\ ; 2K2 |\> | | ; | | | ; /// /// /// ; ; An identical circuit is used for the DATA line. ; Note: The 2N2222 transitors can be replaced with BC337 ( NPN ) which are cheaper ; The keyboard matrix routines are using RB4-RB7 as inputs. ; and RB0-RB2 as output to/from an 3 to 8 multiplexer ; so that it can read up to 4x8 keys ( 32 ). ; ; RA4/TOCK1 is an input from an jumper, if low then keyrepeat is disabled and ; alt-key is enabled instead i.e. instead of repeating a key that has ; been depressed a certain amount of time, a bit is set that can change the ; scancode for a key ( of course, all keys can have an alternate scancode ). ; To exit the alt. keymap press the 'enter alt. keymap' key once again or wait until ; timeout. ( defined in the code ) ; NOTE !! The so called 'enter alt. keymap' key is hardcoded, i.e the key ; i've choosen in this 'example' is Column 1 Row 2, if this is to be changed ; code has to be changed/moved in the debounce and checkkeystate routines. ; ( marked with <-------Alt keymap code-------> ) ; RB3 is currently used for a flashing diode. ( running ) ; Note my real keyboard ( KeyTronic ) uses a clock rate of 40 us, my implementation ; uses about 50 us, easily to change. ; ;*************************************************************************** ; DESCRIPTION ( longer version ) ; ; Pin Used for ; ------------------------------------------------------------ ; RA0 Pc keyboard data out ( to pc ) ; RA1 Pc keyboard data in ( from pc ) ; RA2 Pc keyboard clock in ; RA3 Pc keyboard clock out ; RA4 Flashing disco led 0.5s duty cycle( could be used for some more meaningful purpose ) ; ; RB0 Least significant bit in column adress to 4051 multiplexer ( own keyboard ) ; RB1 Middle... ; RB2 Most significant bit -- || -- ; RB3 Input. Disable keyrepeat & enable alternative keymapping ( if = '0',ground) ; OR if '1' ( free,+V ) enable keyrepeat & disable alternative keymapping ; RB4 Own keyboard input row 1 ; RB5 --- || --- row 2 ; RB6 --- || --- row 3 ; RB7 --- || --- row 4 ; ; 'Basic program structure': ; ; Init - Initialise ports , ram, int, vars ; Start delay - After init the timer int is enabled and the flashing led will ; start to toggle ( flash ). Before I enter the mainloop ; ( and send any keycodes ) I wait until the led has flashed ; twice. This is of course not really needed but I normally ; like to have some kind of start delay ( I know 1 sec is a bit much ) ; Time Int - The timer interrupt (BIG), runs in the background: ; - 'Normal' rate 0.5 ms, when in rx/tx rate is 27 us ; - Performs an rx/tx check, then jumps to rx, tx or 'heart beat' code. ; - TX code sends a byte to pc, at a rate of 27us per int. ; The int rate is actually double the bit rate, as ; a bit is shifted out in the middle of the clock pulse, ; I've seen different implementations of this and I think ; that the bit is not sampled until clock goes low again BUT ; when logging my keyboard ( Keytronic ) this is the way that it ; does it. When all bits are sent, stopbit/parity is sent. ; And the key is removed from the buffer. ; After stopbit/parity is sent, a delay is inserted ( 0.5 ms ) ; before next rx/tx check. ; - RX code recevies a byte from the pc, PIC is controlling clock !! ; Int rate ( 27 us ) is, again, double bit rate. ; Toggles clock and samples the data pin to read a byte from pc. ; When reception is finished an 'handshake' takes place. ; When a byte has been recevied a routine is called to check ; which command and/or data was received. If it was ; keyboard rate/delay or led status byte, it is stored in local ram ; variables. NOTE: The rate/delay is not actually used for ; key repeat as the code is right now ( I use my 'own' fixed rate/delay ) ; however it is very easy to implement. ; After handshake a delay is inserted ( 0.5 ms ) ; before next rx/tx check. ; - 'Heart beat' ( idle code 0.5 ms tick ) performs: ; - Check clock/data lines to see if pc wants to send something or ; if tx is allowed. ; - If tx is possible it checks the keybuffer for an available key and ; if keys are in buffer then it initiates a tx seq. ; and sets the int rate to 27 us. ; - If the pc wants to send something, an rx seq. is initiated ; ( there is some handshaking involved, during which ; the int rate is set to 60 us ) after that, the int rate is ; set to 27 us and an rx seq is started. ; - Divides some clock counters to achive 10ms,100ms,500ms sections. ; - In 100 ms section it performes a numlock status check and ; keyrepeat check ( both rate and delay is local in 100 ms ticks, ; thats why I dont use the 'real' rate delay ) ; - If numlock status is not the desired one code is called to ; toggle the numlock status. ; - If a key has been pressed long enough for repeat, an bit is set ; so we can repeat the key ( send the scancode again ) in the main loop. ; - In 500 ms section the led is toggled on each loop ; - Some various alternative keymap checks to get out of ; alternative keymap. ( i'll get to that in a bit ) ; ; Main loop - Outputs an adress to the 4051 multiplexer waits 1 ms ; reads the row inputs ( 4 bits/keys ), increments address ; and outputs the new adress, waits 1 ms and reads the input ; ( next 4 bits/keys ). Now using the address counter, calls a ; debounce/send routine that first debounces the input, ; ( four consecutive readings before current state is affected ) ; and when a key is changed a make/break code is sent ( put in buffer ). ; In the next loop the next two columns are read etc. until all ; 4 column pairs are read. ; - If keyrepeat is enabled ( see pin conf. above ) the ; repeat flag is checked and if '1' the last pressed key scancode ; is sent again ( put in buffer ). ; - If keyrepeat is not enabled( alternative keymap is enabled instead ) ; then various checks to exit the alternative keymap are performed instead. ; ; Scancodes for all key are located in a lookup table at the end of this file, ; each key has four program rows, to make room for extended codes and alt. keymap codes. ; ; Explanation of 'alternative' keymap: ; ; Using this program ( or an heavily modified version of it anyway ) ; on a computer running Windows posed some small problems; namely: ; -The keyboard ( mapping ) I used did not have any 'special' key such as ; <alt>,<ctrl>,<tab> etc. ; -In windows, things can go wrong, if a dialog pops up or something similar ; there were just no way one could dispose of this with the keymapping i used. ; - 'Only' 28 keys were implemented ( hardware wise ). ; In this particular case the keyrepeat was disabled ( due to the nature of the application ) ; Therefore i came up with the solution to use the keyrepeat related routines and vars. ; To handle a so called 'alternative' keymapping. ; This means that an key is dedicated to be the alt. keymap toggle key, ; when pressing this longer than the programmed repeat delay, instead of ; repeating the key a bit variable is set to use another set of scancodes for ; the keyboard. This 'alternative' keymap is then enabled even though the ; alt. keymap toggle key is released, but it also incorporates an timeout ; that will return to normal keymap if no key is pressed within a certain time ( currently 7 sec ) ; Of course pressing the alt. keymap toggle key again ( while in alt. keymap ) ; will return the keyboard to the normal keymap. ; NOTE !! the key choosen for the alt. keymap toggle is hardcoded, so if the scancode ; for this is changed in the lookup table, changes have to be made in other routines ; as well. ( namly : CHECK_KEY_STATE, and DEBOUNCE_COLUMN routines ) ; While in alt. keymap each key CAN have an alternative scancode ( see lookup table ). ; Also by using the numlock bit, one can toggle numlock status 'on the fly' when entering ; or exiting the alt keymap. ; ; Some notes about the local keyboard interface ( matrix ; Although the hardware circuit and software allows virtually unlimited ; simultaneosly pressed keys, the keyboard matrix itself normally poses ; some limitations on this. If an keymatrix without any protective diodes ; are used then one would have loops INSIDE the keymatrix itself when ; multiple keys are pressed in different columns . ; Look at the ( although horrible ) ASCII art below(internal weak pullup enabled): ; 0 - Key is free ; 1 - Key is pressed ( connection between hor/ver rows ) ; Three keys pressed adressing ( reading ) left column : ; ; To pic1 --------0-------0------ ( row 1 ) ; | | ; To pic2 --------1-------1------ ( row 2 ) ; | | ; To pic3 --------1-------0------ ( row 3 ) ; | | ; Column(4051) 0V - ( Current column is set to 0V when adressing ) ; ; This works as intended, we can read a '0' on pic inputs 2,3 which ; is what we expected. The current ( signal ) follows the route marked with '*': ; ( only the 'signal path' is shown for clarity ) ; ; To pic1 --------0-------0------ ( row 1 ) ; | | ; To pic2 *********-------1------ ( row 2 ) ; * | ; To pic3 *********-------0------ ( row 3 ) ; * | ; Column(4051) 0V - ( Current column is set to 0V when adressing ) ; ; However, when we now read ( address ) the right column instead we ; do not read what is expected ( same three keys still pressed ; ; To pic1 --------0-------0------ ( row 1 ) ; | | ; To pic2 *****************------ ( row 2 ) ; *<- * ; To pic3 *********-------*------ ( row 3 ) ; | * ; Column(4051) - 0V ( Current read column is set to 0V when adressing ) ; ; As you can see the 'signal' travels in the keymatrix itself ( where the '<-' is )and ; will cause a 'ghost' signal to be read on the pic. So instead ; of having an '0' on input 2 only we also can read an '0' on input 3. ; This is because the two keys in column 1 are interconnected ( when they are pressed ). ; Keep this in mind if you are planning to support multiple pressed keys. ; ; ;*************************************************************************** ; ; Some suggestions for 'improvements' or alternations ; ; - Using the jumper 'disable-repeat' as a dedicated key for switching ; to alternative keymapping. ; - Enable repeat in alternative keymapping ; - Clean up TX/RX code ( a bit messy ) ; - Using the led output ( or jumper input ) as an extra adress line ; to the multiplexers ( in this case 2 pcs. 74HCT4051 ) we could have ; 4x16 keys instead. Would require some heavy modifications though ; as there are not much ram/program space left. But if alternative ; keymapping is discarded ( most likely if one has 64 keys ) each ; key in the lookup table only needs to be 2 lines instead of 4. ; That would 'only' require some modifications to preserv ram. ; - Using the EERAM for 'macros' or similar ( not used at all now ) ; ;*************************************************************************** ; ; LEDGEND ; ; I tend to use the following when naming vars. etc. : ; ( yes i DO like long names ) ; ; For 'general' purpose pins: ; ; An input pin is named I_xxx_Name where : ; ; I_ - This is an input pin ; xxx_ - Optional what type of input, jmp=jumper etc. ; Name - Self explanatory ; ; An output pin is named O_xxx_Name where: ; ; O_ - This is an output pin ; xxx_ - Optional what type of output, led=LED etc. ; Name - Self explanatory ; ; Application(function) specific pins: ; ; An application(function) specific pin is named xxName where: ; ; xx - What/Where, for example pc=To/From pc ; Name - Self explanatory ( what does it control etc ) ; ; An #define/constant/equ (no pin or ram ) uses all capital letters e.x. #define BREAK 0xF0 ; ; A bit variable will always start with '_'. For example '_IsLedStatus' ; ; All other ( mostly ramvars. ) are named in various ways. ; ; ;*************************************************************************** TITLE "PC Keyboard emulator - By Tony K&uuml;bek" Processor 16F84 Radix DEC EXPAND ;***** HARDWARE DEFINITIONS ( processor type include file ) INCLUDE <C:\PROGRAM\MPLAB\P16F84.INC> ; this might need changing ! ;***** CONFIGURATION BITS __CONFIG _WDT_ON&_HS_OSC&_CP_OFF&_PWRTE_ON ; _WDT_OFF __IDLOCS 010Bh ; version 1.0B ;***** CONSTANT DEFINITIONS CONSTANT BREAK = 0xF0 ; the break key postfix ( when key is released ) CONSTANT EXTENDED = 0xE0 ; the extended key postfix ; As i dont really use the rate/delay I receive from the pc ( easy to change ) ; this is the current rate/delay times i use: CONSTANT DELAY_ENTER_ALTKEYMAP = 0x1E ; x100 ms , approx 3 seconds ( 30 x 100 ms ) ; how long the 'enter altkeymap' key must ; be in pressed state before the altkeymap is enabled CONSTANT DELAY_EXIT_ALTKEYMAP = 0x0F ; x0.5 sec , approx 7.5 sec ; how long before we exit the alt keymap if no key is ; pressed. CONSTANT DELAY_REPEAT = 0x08 ; x100 ms, approx 800 ms ; how long before we START repeating a key CONSTANT DELAY_RATE = 0x02 ; x100 ms, approx 200 ms repeat rate ; how fast we are repeating a key ( after the delay above ) ;***** CONSTANT DEFINITIONS ( pins ) ; For connection with PC keyboard #define pcCLOCK_in PORTA,2 ; Input PC keyboard clock #define pcCLOCK_out PORTA,3 ; Output PC keyboard clock #define pcDATA_in PORTA,1 ; Input PC keyboard data #define pcDATA_out PORTA,0 ; Output PC Keyboard data ; For connection (input) with our own keyboard ; Note I actually dont use these (definitions!) in the program, but they might come in handy ; at one time or another ( I use the pins though.. ) #define kbROW_1 PORTB,7 #define kbROW_2 PORTB,6 #define kbROW_3 PORTB,5 #define kbROW_4 PORTB,4 ; Indications ( output ) #define O_led_KEYCOMM_ok PORTA,4 ; communication seems ok led ( flashing ) ; Disable/enable key repeat input jumper #define I_jmp_NoRepeat PORTB,3 ; note: internal weak pullup enabled ; For keybuffer ( using the indirect file selector FSR ) #define KeyBufferHead FSR ;***** RAM ASSIGNMENT CBLOCK 0x0C KeyBufferTail ; where the last byte in buffer is.. clkCount ; used for clock timing Offset ; used for table reads Saved_Pclath ; Saved registers during interrrupt Saved_Status ; ----- Saved_w ; ----- CurrKey ; current key ( rx or tx ).. KeyParity ; key parity storage ( inc. for every '1' ) Divisor_10ms ; for the timer Divisor_100ms ; ditto Divisor_500ms ; Divisor_Repeat ; timer for repeated key sends Flags ; various flags RepeatFlags ; flags for repeating a key bitCount ; bitcounter for tx/rx Comm_Flags ; flags used by both rx and tx routines Temp_Var ; temp storage, can be used outside int loop TRX_Flags ; flags used by both rx and tx routines CommandData ; bit map when receving data bytes from pc ; for example led status/ delay / etc KbLedStatus ; to store status led bitmap for keyboard ( pc first sends 'ED' then this byte ) ; bit 0=Scroll lock ( 1=on ) ; bit 1=Num lock ; bit 2=Caps lock ; bits 3-7 = unused KbRateDelay ; to store repeat delay/rate ( pc first sends 'F3' then this byte ) ; bit 0-4 (rate) = '00000' is 30x/sec '11111' is 2x/sec ( i.e. value * 33 ms ) ; bit 5-7 (delay)= '00' is 250 ms, '11' is 1000 ms ( ie. value * 250 ms ) ; bit 7 = unused BufTemp ; temp byte for storing scancode to put in buffer Temp ; temp byte, used locally in buffer routine Temp2 ; LastKey ; stores the last sent key KbBufferMin ; where our keybuffer starts Kb1 ; used in keybuffer Kb2 ; used in keybuffer Kb3 ; used in keybuffer Kb4 ; used in keybuffer Kb5 ; used in keybuffer Kb6 ; used in keybuffer Kb7 ; used in keybuffer Kb8 ; used in keybuffer Kb9 ; used in keybuffer Kb10 ; used in keybuffer KbBufferMax ; end of keybuffer TempOffset ; temporary storage for key offset ( make/break ) LastMakeOffset ; storage of last pressed key ( offset in table ) RepeatTimer ; timer to determine how long a key has been pressed RepeatKey ; the key to repeat repTemp ; temporary storage in repeat key calc. repKeyMap ; bit pattern for the column in which the repeat key is in ; i.e. a copy of kbColumnXX_Old where 'XX' is the column LastKeyTime ; counter when last key was pressed, used to get out of altkeymap ; after a specific 'timeout' kbScan ; scan code for pressed/released key kbTemp ; temp storage for key states kbState ; which keys that has changed in current columns kbBitCnt ; bit counter for key check ( which key/bit ) kbColumnCnt ; column counter ( loops from 8 to 0 ) ; Used as output to multiplexer/decoder and in debounce routines kbColumnVal ; current value of the last 2 columns ( 2x4bits = 8 bits ) input pins ( keys ) ; ; Note the kbColumnXX_New variables is not really needed ; used it while making the program ( debugging ). ; so if more free ram is needed change code using these to use ; the current 'input' sample instead. ( kbColumnVal ) kbColumn12_New ; New debounced reading for column 1 & 2 kbColumn12_Old ; Latest known valid status of column 1 & 2 kbColumn12Cnt ; Debounce counter for column 1 & 2 kbColumn12State ; State of debounce for column 1 & 2 kbColumn34_New ; New debounced reading for column 3 & 4 kbColumn34_Old ; Latest known valid status of column 3 & 4 kbColumn34Cnt ; Debounce counter for column 3 & 4 kbColumn34State ; State of debounce for column 3 & 4 kbColumn56_New ; New debounced reading for column 5 & 6 kbColumn56_Old ; Latest known valid status of column 5 & 6 kbColumn56Cnt ; Debounce counter for column 5 & 6 kbColumn56State ; State of debounce for column 5 & 6 kbColumn78_New ; New debounced reading for column 7 & 8 kbColumn78_Old ; Latest known valid status of column 7 & 8 kbColumn78Cnt ; Debounce counter for column 7 & 8 kbColumn78State ; State of debounce for column 7 & 8 ENDC ;******* END RAM ; *** flags used by both rx and tx routines #define _isParity Comm_Flags,0 ; bit in rx/tx is parity bit #define _KeyError Comm_Flags,1 ; set to '1' when an error is detected #define _isStartBit Comm_Flags,2 ; set to '1' when bit in rx/tx is startbit #define _isStopBit Comm_Flags,3 ; --- || --- but stopbit #define _RxCanStart Comm_Flags,4 ; for handshaking, when negotiating an rx seq. ; *** dito used for rx and tx #define _KeyReceived TRX_Flags,0 ; rx #define _RX_Mode TRX_Flags,1 ; rx is in progress ( started ) #define _doRXAck TRX_Flags,2 ; do rx handshake #define _RXAckDone TRX_Flags,3 ; rx handshake is done #define _RXEnd TRX_Flags,4 ; rx seq is finished #define _RXDone TRX_Flags,5 ; rx exit bit #define _KeySent TRX_Flags,6 ; tx key has been succesfully sent #define _TX_Mode TRX_Flags,7 ; tx is in progress ( started ) ; *** flags to determine the meaning of an incomming data is ; i.e. when then pc has sent a 'data follow' instruction ; these bits are set according to the command, ; i.e. the next incomming byte is..... #define _IsLedStatus CommandData,0 ; the next incoming byte contains kb led status #define _IsRateDelay CommandData,1 ; the next incoming byte contains kb rate/delay #define _SkipByte CommandData,2 ; other data not saved ; *** bit meaning in KbLedStatus ( the leds on the keyboard ) ; i.e local ram copy of this byte #define _LedScrollLock KbLedStatus,0 ; '1' led is on #define _LedNumLock KbLedStatus,1 ; #define _LedCapsLock KbLedStatus,2 ; #define _IsFirstLedStatus KbLedStatus,7 ; set this to '1' at startup, to know that our local ; ; copy (led status) is not yet syncronised. Used to ; ; 'force' sync. the first time we set the local numlock bit. ; *** flags used for various purposes #define _Timer Flags,0 ; used for waiting ;#define _WrongPar Flags,1 ; ?? ( used during dev. to force wrong parity sends, removed ) #define _LastColumn Flags,2 ; for kb scan code #define _isBreak Flags,3 ; if '1' the currently handled key is break ( released ), else make ( pressed ) ;#define _isFree Flags,4 ; Not used ! ( temporary during dev. ) #define _AltKeymap Flags,5 ; is we use alternative keymap ( other scancodes ) see below ! ; enabled ONLY when keyrepeat is disabled ( I_jmp_NoRepeat (RB3) is low ) #define _ExitAltKeymap Flags,6 ; start checking if we should exit alternative keymap ; if all keys are 'free' ( not pressed ) we exit altkeymap #define _InAltKeymap Flags,7 ; if we are waiting for second press/release to get out of altkeymap #define _doRepeat RepeatFlags,0 ; if the last pressed key should be 'repeated' #define _doSendKey RepeatFlags,1 ; send the key in RepeatKey to pc #define _isExtended RepeatFlags,2 ; temp flag if last key is extended #define _RepeatIsExt RepeatFlags,3 ; the key to repeat is extended key #define _startRepeat RepeatFlags,4 ; start repeat timer #define _DoExitAltKeymap RepeatFlags,5 ; bit set when we are getting out of alternative keymap #define _NumLock RepeatFlags,6 ; 'mirror' of numlockstatus, by setting/clearing this bit ; numlock status will be changed. ; I.e. there is no need to 'manually' send break/make code for numlock ; key, by setting this bit to '1' numlock status will by automaticlly ; ( inside int handler ) set to 'on'( within 100 ms ). Se code for example. #define _WaitNumLock RepeatFlags,7 ; bit set when we have sent make/break numlock scancode ; and waiting for numlock status byte reply. ; ( to inhibit a new numlock scancode send ) ;************************************************************************** ; Macros ;************************************************************************** ;+++++ ; BANK0/1 selects register bank 0/1. ; Leave set to BANK0 normally. BANK0 MACRO BCF STATUS,RP0 ENDM BANK1 MACRO BSF STATUS,RP0 ENDM ;+++++ ; PUSH/PULL save and restore W, PCLATH and STATUS registers - ; used on interrupt entry/exit PUSH MACRO MOVWF Saved_w ; Save W register on current bank SWAPF STATUS,W ; Swap status to be saved into W BANK0 ; Select BANK0 MOVWF Saved_Status ; Save STATUS register on bank 0 MOVFW PCLATH MOVWF Saved_Pclath ; Save PCLATH on bank 0 ENDM PULL MACRO BANK0 ; Select BANK0 MOVFW Saved_Pclath MOVWF PCLATH ; Restore PCLATH SWAPF Saved_Status,W MOVWF STATUS ; Restore STATUS register - restores bank SWAPF Saved_w,F SWAPF Saved_w,W ; Restore W register ENDM ;+++++ ; We define a macro that will switch an output pin on or off depending ; on its previous state. We must be on bank0 !! ; TOGGLE_PIN MACRO WHICH_PORT,WHICH_PIN LOCAL TOGGLE_PIN10, TOGGLE_END BTFSC WHICH_PORT,WHICH_PIN ; is the pin high ? GOTO TOGGLE_PIN10 ; yes, clear it BSF WHICH_PORT,WHICH_PIN ; no, so set it GOTO TOGGLE_END TOGGLE_PIN10: BCF WHICH_PORT,WHICH_PIN ; clear the pin TOGGLE_END: ENDM ;************************************************************* ; Credit for routine ( almost unchanged, expect it's now a macro ) ; goes to Scott Dattalo http://www.interstice.com/~sdattalo/technical/software/software.html ; ( debouce routine at http://www.interstice.com/~sdattalo/technical/software/pic/debounce.html ) ; ; DEBOUNCE_BYTE - 'debounces' 8 bits, when a bit state ; has been 'active' for 4 consecutive debounce loops ; it's state is put into the 'debounced' sample ( i.e. output ) ; ; The purpose of this routine is to debounce, i.e. digitally low pass filter ; inputs. The algorithm handles upto 8 bits at a time. An input is considered ; filtered if it has not changed states in the last 4 samples. ; ; 2-bit cyclic vertical counters count the 4 samples. As long as there is no ; change, the counters are held in the reset state of 00b. When a change is detected ; between the current sample and the filtered or debounced sample, the counters ; are incremented. The counting sequence is 00,01,10,11,00... When the counters ; roll over from 11b to 00b, the debounced state is updated. If the input changes ; back to the filtered state while the counters are counting, then the counters ; are re-initialized to the reset state and the filtered state is unaffected. ; In other words, a glitch or transient input has been filtered. ; ; Here's the C-psuedo code: ; ;static unsigned clock_A,clock_B,debounced_state ; ;debounce(unsigned new_sample) ;{ ; unsigned delta; ; ; delta = new_sample ^ debounced_state; //Find all of the changes; ; clock_A ^= clock_B; //Increment the counters ; clock_B = ~clock_B; ; ; clock_A &= delta; //Reset the counters if no changes ; clock_B &= delta; //were detected. ; ; //Preserve the state of those bits that are being filtered and simultaneously ; //clear the states of those bits that are already filtered. ; debounced_state &= (clock_A | clock_B); ; //Re-write the bits that are already filtered. ; debounced_state |= (~(clock_A | clock_B) & new_sample); ;} ; ; The 2-bit counters are arranged "vertically". In other words 8 counters ; are formed with 2 bytes such that the corresponding bits in the bytes are ; paired (e.g. MSBit of each byte is paired to form one counter). ; The counting sequence is 0,1,2,3,0,1,... And the state tables and Karnaugh ; maps are: ; ;State Table: Karnaugh Maps: ;pres next B ; SS SS 0 1 ; AB AB +---+---+ +---+---+ ;-------- A 0| | 1 | | 1 | | ; 00 01 +---+---+ +---+---+ ; 01 10 1| 1 | | | 1 | | ; 10 11 +---+---+ +---+---+ ; 11 00 A+ = A ^ B B+ = ~B ; ; Here's the PIC code that implements the counter: ; MOVF SB,W ;W = B ; XORWF SA,F ;A+ = A ^ B ; COMF SB,F ;B+ = ~B ; 14 instructions ; 15 cycles ; Inputs: ; NewSample - The current sample ; Outputs ; DebouncedSample - The current value (filtered version of NewSample) ; ; VARS used ; DBCnt, ; DBState - State variables for the 8 2-bit counters ; DEBOUNCE_BYTE MACRO NewSample,DebouncedSample,DBCnt,DBState ;Increment the vertical counter MOVF DBState,W XORWF DBCnt,F COMF DBState,F ;See if any changes occurred MOVF NewSample,W XORWF DebouncedSample,W ;Reset the counter if no change has occurred ANDWF DBState,F ANDWF DBCnt,F ;Determine the counter's state MOVF DBState,W IORWF DBCnt,W ;Clear all bits that are filtered-or more accurately, save ;the state of those that are being filtered ANDWF DebouncedSample,F XORLW 0xff ;Re-write the bits that haven't changed. ANDWF NewSample,W IORWF DebouncedSample,F ENDM ;************************************************************************** ; Program Start ;************************************************************************** ; Reset Vector ORG H'00' ; For the sole purpose of squeezing every last byte of the programming mem ; I actually use the 3 program positions before the interrupt vector ; before jumping to the main program. Take note though that ; ONLY 3 instructions are allowed before the jump to main loop !! BANK0 CLRF PCLATH CLRF INTCON GOTO INIT ;************************************************************************** ; Interrupt routine ; An humongously big int handler here ; but the keyboard handling is FULLY in the background, without any intervention ; in the main loop whatsoever. I like the solution anyway. ; ;************************************************************************** ; Interrupt vector ORG H'04' INT PUSH ; Save registers and set to BANK 0 BTFSS INTCON,T0IF ; check if TMR0 interrupt GOTO INTX ; whoops ! 'unknown' int, should be disabled... ; NOTE ! if an 'unknown' int triggers the int routine ; the program will loop here for ever ( as the calling flag is not cleared ) ;+++ ; Timer (TMR0) timeout either heart beat or tx/rx mode ; In 'heart beat mode' we monitor the clock and data lines ; at ( roughly )= 0.5 ms interval, we also check the send buffer ; if there are any keys to send to pc ( if clock/data levels allows us to ) ; In tx/rx mode we are controlling the clock/data line = 27 us tick ; Note: This software works using both 8Mhz and 10Mhz resonators without modifications ; however the 'timing' will then of course be 'off'. INT_CHECK BCF INTCON,T0IF ; Clear the calling flag ! BTFSC _TX_Mode ; check if we are in tx mode GOTO INT_TX ; yep, goto tx mode code.. BTFSC _RX_Mode ; are we in rx mode ? GOTO INT_RX ; yep goto rx mode code GOTO INT_HEART_BEAT ; nope just goto 'heart beat' mode ;*************** TX code start *********************************** INT_TX MOVLW H'FA' ; preset timer with 252 ( 256 - 6 = 250 ) ; timetick = 0.4uS x 8 ( prescale ) x 6 + 8us ( int handling code )= 27 us MOVWF TMR0 BTFSS clkCount,0 ; check if we should toggle clock GOTO INT_DEC_CLOCK ; bit low,decrement and check if we should toggle data instead DECFSZ clkCount,F ; decrement and check if we are at zero.. GOTO INT_CLOCK ; not zero then toggle clock line GOTO INT_EXIT_TX INT_CLOCK BTFSC pcCLOCK_out ; check if we are low GOTO INT_CLOCK_HIGH ; yep set to high BTFSS pcCLOCK_in ; check if pc is pulling the clock line low ; i.e. it wants to abort and send instead.. GOTO INT_TX_CHECK_ABORT ; abort this transfer BSF pcCLOCK_out ; ok to set clock low ( pull down ) GOTO INTX INT_CLOCK_HIGH BCF pcCLOCK_out ; set high ( release line ) ;BCF _ClockHigh ; GOTO INTX INT_TX_CHECK_ABORT GOTO INT_EXIT_TX INT_DEC_CLOCK DECF clkCount,F ; decrement clock counter ( so we toggle next time ) INT_DATA BTFSS bitCount,0 ; check bit counter GOTO INT_DATA_IDLE ; no data toggle DECFSZ bitCount,F ; decrement bit counter GOTO INT_DATA_NEXT ; next bit.. INT_NO_BITS BSF bitCount,0 ; just in case ( stupid code, not sure its needed, just ; to make it impossible to overdecrement )*** BTFSC _isParity ; are we sending parity ? GOTO INT_DATA_END ; exit ; all bits sent ; delete the last key from the buffer CALL INC_KEY_HEAD ; remove the ( last ) key form the buffer as is was sent ok.. ; all bits sent check parity BSF _isParity ; set flag data is parity BTFSS KeyParity, 0 ; is the last parity bit high? ( odd num of bits ) ; then parity should be high ( free ) GOTO INT_DATA_HIGH_PAR ; yes BSF pcDATA_out ; no, parity should be 'low' ( pulled down ) GOTO INTX ; INT_DATA_HIGH_PAR: BCF pcDATA_out ; set parity bit high ( release data line ) GOTO INTX ; and exit.. INT_DATA_END BTFSS _isStopBit ; is the stopbit sent ? GOTO INT_DATA_STOPB ; nope then set stopbit flag BCF pcDATA_out ; parity bit sent, always release data line ( stop bit ) GOTO INTX INT_DATA_STOPB BSF _isStopBit ; set the stopbit flag GOTO INTX ; and exit INT_DATA_IDLE DECF bitCount,F ; decrement bit counter GOTO INTX ; no toggle of data line INT_DATA_NEXT BTFSS CurrKey,0 ; is the last bit of the key_buffer high? GOTO INT_DATA_LOW ; no, pull data low BCF pcDATA_out ; yes, release data line INCF KeyParity,F ; increment parity bit GOTO INT_DATA_ROTATE ; rotate data INT_DATA_LOW ; last bit is low BSF pcDATA_out ; set the bit INT_DATA_ROTATE RRF CurrKey,F ; rotate right by 1 bit GOTO INTX INT_EXIT_TX ; setup the timer so we accomplish an delay after an tx seq BCF _TX_Mode ; clear tx mode flag BCF pcCLOCK_out ; release clock line BCF pcDATA_out ; and data line ; MOVLW H'64' ; start timer with 100 ( 256-100 = 156 ) ; timetick = 0.4uS x 8 ( prescale ) x 156 = (roufly) 0.5 mS MOVWF TMR0 GOTO INTX ;************** TX code end ***************** ;************** RX code start *************** INT_RX MOVLW H'FA' ; preset timer with 252 ( 256 - 6 = 250 ) ; timetick = 0.4uS x 8 ( prescale ) x 6 + 8us ( int handling code )= 27 us MOVWF TMR0 BTFSS clkCount,0 ; check if we should toggle clock GOTO INT_RX_DEC_CLOCK ; bit low,decrement and check if we should read data instead DECFSZ clkCount,F ; decrement and check if we are at zero.. GOTO INT_RX_CLOCK ; not zero then toggle clock line BCF pcCLOCK_out ; release the clock line we are done.. BCF _RX_Mode ; clear rx mode bit ( go over to heart beat mode ) GOTO INT_EXIT_RX INT_RX_CLOCK BTFSC pcCLOCK_out ; check if we are low GOTO INT_RX_CLOCK_HIGH ; yep set to high ( release line ) BTFSC _isStartBit ; check if this is the first bit ( start ) GOTO INT_RX_START ; clear start bit and continue BTFSC _isParity ; check if this is the parity bit ( or parity has been received ) GOTO INT_RX_PAR ; yep check parity GOTO INT_RX_BIT ; ok just a 'normal' bit read it INT_RX_PAR ; check parity BTFSC _doRXAck ; check the handshake flag GOTO INT_RX_HNDSHK ; start handshake check BTFSS pcDATA_in ; is the input high ? GOTO INT_RX_PAR_HIGH ; yep BTFSC KeyParity,0 ; is the parity '0' ( should be ) GOTO INT_RX_PAR_ERR ; nope parity error GOTO INT_RX_ACK ; parity ok next should be ack ( we take data line low ) INT_RX_PAR_HIGH BTFSS KeyParity,0 ; check that parity bit is '1' GOTO INT_RX_PAR_ERR ; nope parity error GOTO INT_RX_ACK ; parity ok, next is ack ( we take data line low ) INT_RX_PAR_ERR BSF _KeyError ; set error flag INT_RX_ACK BSF pcCLOCK_out ; ok to set clock low ( pull down ) BSF _doRXAck ; enable ack check GOTO INTX INT_RX_HNDSHK BTFSS _RXEnd ; if we are done dont take data low BSF pcCLOCK_out ; ok to set clock low ( pull down ) BTFSC _RXAckDone ; chek if hand shake ( ack ) is done ? BSF _RXEnd ; ok we are now done just make one more clock pulse GOTO INTX ; exit INT_RX_CLOCK_HIGH BCF pcCLOCK_out ; set high ( release line ) BTFSS _RXAckDone ; are we done.. ? GOTO INTX BTFSS _RXDone ; finished ? GOTO INTX BCF _RX_Mode ; and clear rx flag.. GOTO INT_EXIT_RX ; bye bye baby INT_RX_DEC_CLOCK DECF clkCount,F ; decrement clock counter ( so we toggle next time ) BTFSS _doRXAck ; check if we are waiting for handshake GOTO INTX BTFSC pcCLOCK_out ; check if the clock is low ( pulled down ) GOTO INTX ; nope we are pulling down then exit ; we only take over the data line if ; the clock is high ( idle ) ; not sure about this though.. ??? BTFSC _RXEnd ; are we done ? GOTO INT_RX_END ; handshake check if data line is free ( high ) BTFSS pcDATA_in ; is data line free ? GOTO INTX ; nope BSF pcDATA_out ; takeover data line BSF _RXAckDone ; we are done..at next switchover from low-high we exit GOTO INTX ; INT_RX_END BCF pcDATA_out ; release data line BSF _RXDone ; we are now done GOTO INTX INT_RX_START BCF _isStartBit ; clear start bit flag BSF pcCLOCK_out ; ok to set clock low ( pull down ) GOTO INTX INT_RX_BIT BCF CurrKey,7 BTFSS pcDATA_in ; is bit high GOTO INT_RX_NEXT ; nope , it's a '0' BSF CurrKey,7 ; set highest bit to 1 INCF KeyParity,F ; increase parity bit counter INT_RX_NEXT BSF pcCLOCK_out ; ok to set clock low ( pull down ) DECFSZ bitCount,F ; decrement data bit counter GOTO INT_RX_NEXT_OK BSF bitCount,0 ; just in case ( so we cannot overdecrement ) BSF _isParity ; next bit is parity GOTO INTX INT_RX_NEXT_OK CLRC ; clear carry, so it doesnt affect receving byte RRF CurrKey,F ; rotate to make room for next bit GOTO INTX ; and exit INT_EXIT_RX ; handle the recevied key ( if not it is an 'data' byte ) MOVLW H'C4' ; preset timer with 196 ( 256 - 60 = 196 ) ; timetick = 0.4uS x 8 ( prescale ) x 60 + 8 ( int handling code )= 200 us ; MOVWF TMR0 ; this delay seems to be needed ( handshake ? ) ; check if this is an data byte ( rate/delay led status etc ) MOVF CommandData,F ; reload into itself ( affect zero flag ) BTFSS STATUS,Z ; check zero flag GOTO INT_STORE_DATA ; byte contains data ( rate/delay etc ) CALL CHECK_RX_KEY ; no data, handle recevied command GOTO INTX INT_STORE_DATA ; store data byte in 'currkey', ; first reply with 'ack' MOVLW H'FA' ; keyboard ack CALL ADD_KEY ; BTFSS _IsLedStatus ; is it led status byte ? GOTO INT_STORE_RATE ; nope check next INT_STORE_NUM ; byte in 'currkey' is led status byte, store it MOVF CurrKey,W ; get byte MOVWF KbLedStatus ; and store it BTFSC _WaitNumLock ; was this something we were waiting for ? ; i.e. we sent the make scancode for numlock. CALL RELEASE_NUMLOCK ; yep, then send release code for 'soft' numlock GOTO INT_STORE_EXIT ; store it in local ram copy and exit INT_STORE_RATE BTFSS _IsRateDelay ; is it rate/delay byte ? GOTO INT_STORE_EXIT ; nope then send ack end exit ; byte in 'currkey' is rate/delay byte, store it MOVF CurrKey,W ; get byte MOVWF KbRateDelay ; and store it INT_STORE_EXIT CLRF CommandData ; clear data byte flags GOTO INTX ;******************* 'heart' beat code ( 0.5 ms tick ) ********* ; Note: As I 'mess' with the int timer this 'heart beat' is by no means ; an accurate clock. However it can work as a rough estimate for local time. ; INT_HEART_BEAT MOVLW H'64' ; start timer with 100 ( 256-100 = 156 ) ; timetick = 0.4uS x 8 ( prescale ) x 156 = (roufly) 0.5 mS MOVWF TMR0 INT_CHECK_CLKDTA: ; CLOCK DATA Action ;-----------+-------- ; L L | wait ? ; L H | wait, buffer keys ; H L | start an rx sequence ; H H | keyboard can tx BTFSS pcDATA_in ; is the data line high ( free ).. GOTO INT_CHECK_RX ; Nope it's pulled down, check if rx is requested BTFSC pcCLOCK_in ; Is the clk line low ( pulled down ) ? GOTO INT_CHECK_BUFF ; Nope, so check if we have any keys to send GOTO INT_IDLE ; clock is low , wait and buffer keys( i.e. no rx/tx ) INT_CHECK_RX ; pc ( probably ) wants to send something.. BTFSS pcCLOCK_in ; wait until clock is released before we go into receving mode.. GOTO INT_RX_IDLE ; nope still low ; clock now high test if we are set to start an rx seq. BTFSS _RxCanStart ; have we set the flag ? GOTO INT_WAIT_RX ; nope then set it BTFSC pcDATA_in ; make sure that data still is low GOTO INT_ABORT_RX ; nope abort rx req, might been a 'glitch' ; initiate the rx seq. CLRF Comm_Flags ; used by both tx/rx routines ( and _RxCanStart bit !! ) CLRF TRX_Flags ; clear tx/rx flags CLRF KeyParity ; clear parity counter BSF _RX_Mode ; set rx mode flag.. BSF _isStartBit ; set that next sampling is start bit ; preset bit and clock counters MOVLW H'2F' ; = 47 dec, will toggle clock output every even number until zero MOVWF clkCount ; preset clock pulse counter MOVLW H'08' ; = 8 dec, number of bits to read ; then parity bit will be set instead MOVWF bitCount ; preset bit counter ; note as we are starting the clock here we allow a longer time before we start ; the actual 20 us tick, this the first time we wait about 200 us before the first clock 'tick' MOVLW H'C4' ; preset timer with 196 ( 256 - 60 = 196 ) ; timetick = 0.4uS x 8 ( prescale ) x 60 + 8us ( int handling code )= 200 us ; MOVWF TMR0 GOTO INTX ; exit, the next int will start an rx seq INT_WAIT_RX: BSF _RxCanStart ; set flag so we start rx next int ( 0.5 ms ) INT_RX_IDLE ; reload clock so we check more often MOVLW H'F0' ; start timer with 240 ( 256-16 = 240 ) ; timetick = 0.4uS x 8 ( prescale ) x 16 + 8/10us ( int handling code )= (about) 60 us MOVWF TMR0 GOTO INTX ; INT_ABORT_RX BCF _RxCanStart ; clear flag ( forces a 'new' rx start delay ) GOTO INT_IDLE ; INT_CHECK_BUFF: ; check if we have any keys to send to pc MOVF KeyBufferTail,W ; get end of buffer to 'w' SUBWF KeyBufferHead,W ; subtract start of buffer if head - tail = zero, no byte BTFSC STATUS, Z ; zero flag = no byte to send GOTO INT_IDLE ; then do the 'idle' stuff INT_SEND_KEY ;key in buffer, get it and initiate an tx seq... CALL GET_KEY_BUFFER ; get the key into CurrKey MOVF CurrKey,W MOVWF LastKey ; store last sent key ; setup our tx/rx vars CLRF Comm_Flags ; used by both tx/rx routines ( and _RxCanStart bit !! ) CLRF TRX_Flags ; clear tx/rx flags CLRF KeyParity ; clear parity counter BSF _TX_Mode ; set tx mode flag.. ; preset bit and clock counters MOVLW H'2B' ; = 43 dec, will toggle clock out put every even number until zero MOVWF clkCount ; preset clock pulse counter MOVLW H'12' ; = 18 dec, will shift data out every even number until zero ; then parity bit will be set instead MOVWF bitCount ; preset bit counter ; data now set, initiate the clock to generate a 20 us tick ( rx/tx heart beat ) BSF pcDATA_out ; start bit, always 'low' ( we pull down ) MOVLW H'FA' ; start timer with 252 ( 256-6 = 250 ) ; timetick = 0.4uS x 8 ( prescale ) x 6 + 8 ( int handling code )= 27 us MOVWF TMR0 GOTO INTX ; exit, the next int will start an tx INT_IDLE: ; 'Idle' code i.e. no rx or tx possible AND/OR nothing to send DECF Divisor_10ms,F ; Count 0.5ms down to give 10 milli second tick BNZ INTX ; Exit if divider not zeroed MOVLW .20 MOVWF Divisor_10ms ; Preset the divide by 20 ;+++ ; 10 ms tick here ; Divide the 10 ms tick to give 100 ms second tick INT_10MS DECF Divisor_100ms,F ; Count 10ms down to give 100 milli second tick BNZ INTX ; Exit if divider not zeroed MOVLW .10 MOVWF Divisor_100ms ; Preset the divide by 10 ;+++ ; 100 ms tick here INT_100MS ; numlock check !! bit variable _Numlock does not actually set the numlock status directly. ; However, by setting this bit to '1' we make a test against the current numlock led status ; ( bit no 1 in variable KbLedStatus ), if that is different from this variable ; we send a 'numlock' press/release ( to toggle numlock status ) ; so in essence, setting this bit to '1' will force numlock to be 'on' etc. BTFSC _IsFirstLedStatus ; if = 1 then we have not yet received numlock status GOTO INT_REPEAT_CHECK ; nope, then this is a consecutive byte, store as 'normal' BTFSS _WaitNumLock ; are we waiting for pc numlock reply ? GOTO INT_NUMLOCK_CHECK ; yep then do repeat check instead DECFSZ Temp_Var,F ; GOTO INT_REPEAT_CHECK CALL RELEASE_NUMLOCK INT_NUMLOCK_CHECK BTFSC _LedNumLock ; is the led on ? GOTO INT_NUMLOCK_ON ; yep, then test our 'local' numlock state ( wanted numlock state ) ; nope numlock is off, is our wanted state also off ? BTFSS _NumLock ; is wanted state off ? GOTO INT_REPEAT_CHECK ; yep continue CALL PRESS_NUMLOCK ; nope then send numlock press/release code GOTO INT_REPEAT_CHECK INT_NUMLOCK_ON BTFSC _NumLock ; is wanted state also 'on' ? GOTO INT_REPEAT_CHECK ; yep CALL PRESS_NUMLOCK ; nope then toggle numlock state INT_REPEAT_CHECK ; check if a key should be 'repeated' ( when pressed longer than 500 ms ) BTFSS _startRepeat ; start repeating a key ? ( delay !!! ) GOTO INT_CHECK_KEY ; nope, then check if key should be repeated DECF RepeatTimer,F ; BNZ INT_500MS ; not zero yet, check timer instead BCF _startRepeat ; stop repeat timer ( delay is accomplished ) BSF _doRepeat ; and enable 'key' is still down check MOVLW .02 ; start repeat send timer MOVWF Divisor_Repeat ; GOTO INT_500MS ; do next timer check INT_CHECK_KEY BTFSS _doRepeat ; key should be repeated ? GOTO INT_500MS ; nope ; ok key should be repeated, check if it still pressed ? CALL CHECK_KEY_STATE ; uses MakeKeyOffset to calculate which key that was ; the last pressed, and then check if it's still pressed ; if still pressed carry = '1', BTFSS STATUS,C ; check carry BCF _doRepeat ; clear repeat bit, stop repeating the key BTFSS _doRepeat ; still pressed ? GOTO INT_500MS ; nope DECF Divisor_Repeat,F ; should we send the key ? BNZ INT_500MS ; nope MOVLW DELAY_RATE ; reload timer with key rate delay ;MOVLW .02 ; restart timer MOVWF Divisor_Repeat ; BSF _doSendKey ; set flag to send key, NOTE the actual sending ( putting into send buffer ) ; is done inside mainloop. INT_500MS DECF Divisor_500ms,F ; Count 100ms down to give 500 milli second tick BNZ INTX ; Exit if divider not zeroed MOVLW .05 MOVWF Divisor_500ms ; Preset the divide by 5 ;+++ ; 500 ms tick here INT_500_NEXT TOGGLE_PIN O_led_KEYCOMM_ok ; toggle the disco light BTFSS _DoExitAltKeymap ; is the alt keymap toggle key pressed the second time ? ; if so skip timeout test and exit BTFSS _InAltKeymap ; are we in altkeymap ? GOTO INTX ; nope ; we are in altkeymap, decrement the lastkeytime ; and check if we are at zero then we exit ; the altkeymap. DECF LastKeyTime,F ; decrease time BNZ INTX ; exit, timer has not expired ; timer expired, get out of altkey map BSF _ExitAltKeymap ; ; ***************** 'heart' beat code end *************** INTX ;BCF INTCON,T0IF ; Clear the calling flag PULL ; Restore registers RETFIE ; **************** end interrupt routine ************** ;+++++ ; Routines that will 'toggle' keyboard numlock status ; by sending numlock make/break code ; PRESS_NUMLOCK: MOVLW H'77' ; numlock key scancode, make CALL ADD_KEY MOVLW H'06' ; 6 x 100 ms = 600 ms ( release delay ) MOVWF Temp_Var ; BSF _WaitNumLock ; we are waitin for numlock status reply from pc RETURN RELEASE_NUMLOCK: MOVLW BREAK ; break prefix CALL ADD_KEY MOVLW H'77' ; numlock key scancode CALL ADD_KEY BCF _WaitNumLock RETURN ; *********************************************************************** ; ; CHECK_RX_KEY - handles the received commands from pc ; CHECK_RX_KEY ; check the key in 'currkey' ( command from pc ) CHECK_ED MOVF CurrKey,W ; move key buffer into W register SUBLW H'ED' ; subtract value in W with 0xED BTFSS STATUS, Z ; check if the zero bit is set GOTO CHECK_EE ; the result of the subtraction was not zero check next ; ok 'ED'=set status leds ( in next byte ) received BSF _IsLedStatus ; set bit that next incoming byte is kb led staus GOTO CHECK_SEND_ACK ; send ack CHECK_EE MOVF CurrKey,W ; move key buffer into W register SUBLW H'EE' ; subtract value in W with 0xEE BTFSS STATUS, Z ; check if the zero bit is set GOTO CHECK_F0 ; the result of the subtraction was not zero check next ; ok 'EE'= echo command received GOTO CHECK_SEND_EE ; send echo CHECK_F0 MOVF CurrKey,W ; move key buffer into W register SUBLW H'F0' ; subtract value in W with 0xF0 BTFSS STATUS, Z ; check if the zero bit is set GOTO CHECK_F2 ; the result of the subtraction was not zero check next ; ok 'F0'= scan code set ( in next commming byte ) received BSF _SkipByte ; skip next incomming byte ( or dont interpret ) GOTO CHECK_DONE ; do not send ack ! CHECK_F2 MOVF CurrKey,W ; move key buffer into W register SUBLW H'F2' ; subtract value in W with 0xF0 BTFSS STATUS, Z ; check if the zero bit is set GOTO CHECK_F3 ; the result of the subtraction was not zero check next ; ok 'F2'= Read ID command responds with 'AB' '83' GOTO CHECK_SEND_ID ; send id bytes CHECK_F3 MOVF CurrKey,W ; move key buffer into W register SUBLW H'F3' ; subtract value in W with 0xF3 BTFSS STATUS, Z ; check if the zero bit is set GOTO CHECK_FE ; GOTO CHECK_F4 ; the result of the subtraction was not zero check next ; ok 'F3'= set repeat rate ( in next commming byte ) received BSF _IsRateDelay ; next incomming byte is rate/delay info GOTO CHECK_SEND_ACK ; send ack ; **** Note ! removed from test as I 'don't care' ******** ; **** i.e. I dont disable or enable the keyboard at any time. ;CHECK_F4 ; MOVF CurrKey,W ; move key buffer into W register ; SUBLW H'F4' ; subtract value in W with 0xF4 ; BTFSS STATUS, Z ; check if the zero bit is set ; GOTO CHECK_F5 ; the result of the subtraction was not zero check next ; ok 'F4'= keyboard enable received ; GOTO CHECK_SEND_ACK ; send ack ;CHECK_F5 ; MOVF CurrKey,W ; move key buffer into W register ; SUBLW H'F5' ; subtract value in W with 0xF5 ; BTFSS STATUS, Z ; check if the zero bit is set ; GOTO CHECK_FE ; the result of the subtraction was not zero check next ; ok 'F5'= keyboard disable received ; GOTO CHECK_SEND_ACK ; send ack CHECK_FE MOVF CurrKey,W ; move key buffer into W register SUBLW H'FE' ; subtract value in W with 0xFE BTFSS STATUS, Z ; check if the zero bit is set GOTO CHECK_FF ; the result of the subtraction was not zero check next ; ok 'FE'= resend last sent byte MOVF LastKey,W ; get last key CALL ADD_KEY ; and put it on the que GOTO CHECK_DONE CHECK_FF MOVF CurrKey,W ; move key buffer into W register SUBLW H'FF' ; subtract value in W with 0xFF BTFSS STATUS, Z ; check if the zero bit is set GOTO CHECK_ERROR ; the result of the subtraction was not zero, unknown command ; ok 'FF'= reset keyboard received GOTO CHECK_SEND_AA ; send 'AA' power on self test passed CHECK_ERROR ; unknown command ( or command not interpreted ) GOTO CHECK_SEND_ACK CHECK_SEND_ID MOVLW H'FA' ; keyboard ack CALL ADD_KEY ; MOVLW H'AB' ; keyboard id first byte, always 0xAB CALL ADD_KEY ; MOVLW H'83' ; keyboard id second byte, always 0x83 CALL ADD_KEY ; GOTO CHECK_DONE CHECK_SEND_ACK MOVLW H'FA' ; keyboard ack CALL ADD_KEY ; GOTO CHECK_DONE CHECK_SEND_AA MOVLW H'FA' ; keyboard ack CALL ADD_KEY ; MOVLW H'AA' ; keyboard post passed CALL ADD_KEY ; GOTO CHECK_DONE CHECK_SEND_EE MOVLW H'EE' ; keyboard echo CALL ADD_KEY ; CHECK_DONE RETLW 0 ; and we are done ; *********************************************************************** ; Buffer code ( a bit modified ) from Stewe Lawther ; http://ourworld.compuserve.com/homepages/steve_lawther/ucindex.htm ; And of course source of the exellent keyboard viewer. !! ( without which, this ; project would have been close to impossible ) ( and of course my nifty ; memory oscilloscope ) ; ; ADD_KEY_BUFFER - (outside int)add the key in CurrKey to our keybuffer que in the first ; free position. If there is no more room the oldest byte is ; 'dumped'. ; ADD_KEY - Same but to be used inside int routine.( just skips int disable code ) ADD_KEY_BUFFER ADD_STOP_INT ; first stop all interrupts !!!!!!! BCF INTCON,GIE ; disable global interrupts.. BTFSC INTCON,GIE ; check that is really was disabled GOTO ADD_STOP_INT ; nope try again ADD_KEY ; inside interuppt we call this instead ( as we dont need to disable int ) MOVWF BufTemp ; store key temporary MOVF KeyBufferHead,W ; move buffer head out of FSR temporarily MOVWF Temp ; store in temp MOVF KeyBufferTail,W ; set FSR to buffer tail MOVWF FSR ; set indirect file pointer MOVF BufTemp,W ; set W to new scancode to send MOVWF INDF ; and put it in the buffer MOVF Temp,W ; get the head pointer back MOVWF KeyBufferHead ; INCF KeyBufferTail,W ; get the end of the buffer SUBLW KbBufferMax ; check if at buffer max INCF KeyBufferTail,W ; (reload value to w - doesn't affect C) BTFSS STATUS, C ; if so (negative result) MOVLW KbBufferMin ; set to buffer min ( wrap around ) MOVWF KeyBufferTail SUBWF KeyBufferHead,W ; see if we have any room ( head and tail have meet ) BTFSC STATUS, Z ; if so (Z set) CALL INC_KEY_HEAD ; dump oldest byte ; finally turn on interrupts again MOVLW b'10100000' ; enable global & TMR0 interrupts MOVWF INTCON RETURN ; *********************************************************************** ; ; GET_KEY_BUFFER - Gets a char from the buffer, and puts it into KeyBuffer ; NOTE: Does not increase buffer pointers ( dump this key ). ; A call to INC_KEY_HEAD will do this if the key is sent ok GET_KEY_BUFFER MOVF INDF, W ;put the byte to send into key buffer MOVWF CurrKey RETURN ; and go back, NOTE ! the key is not ; removed from the buffer until a call ; to INC_KEY_HEAD is done. ; *********************************************************************** ; ; INC_KEY_HEAD - dump oldest byte in keybuffer, Do not call if byte ; has not been fetched before ( GET_KEY_BUFFER ) ; INC_KEY_HEAD INCF KeyBufferHead,W ; set to next byte in buffer SUBLW KbBufferMax ; check if at buffer max INCF KeyBufferHead,W ; (reload value to w - doesn't affect C) BTFSS STATUS, C ; if so (negative result) MOVLW KbBufferMin ; set to buffer min ( wrap around ) MOVWF KeyBufferHead ; and store ( in FSR ) RETURN ; go back ; *********************************************************************** ; ; CHECK_KEY_STATE - Check if the last pressed key is still pressed ; Returns with carry = '1' if still pressed ; else carry = '0' ( or error ) ; CHECK_KEY_STATE: ; uses LastMakeOffset to calculate which key to test MOVF LastMakeOffset,W ; get offset ANDLW H'18' ; mask out column bits ; lastmake offset has the following bits: ; '000yyxxx' where 'yy' is column no ; and 'xxx' is key num, BTFSC STATUS,Z ; zero = column 1 & 2 GOTO CHECK_COL_12 ; it is in column 1 MOVWF repTemp ; save it temporary SUBLW H'08' ; subtract value in W with 0x08 ( columns 3 & 4 ) BTFSC STATUS, Z ; check if the zero bit is set GOTO CHECK_COL_34 ; it is in column 3 & 4 MOVF repTemp,W ; get the column bits back SUBLW H'10' ; subtract value in W with 0x10 ( columns 5 & 6 ) BTFSC STATUS, Z ; check if the zero bit is set GOTO CHECK_COL_56 ; it is in column 5 & 6 CHECK_COL_78 MOVF kbColumn78_Old,W ; get bit map ( key status ) for keys in column 7 & 8 MOVWF repKeyMap ; and store it GOTO CHECK_KEY ; and continue to check bit CHECK_COL_56 MOVF kbColumn56_Old,W ; get bit map ( key status ) for keys in column 5 & 6 MOVWF repKeyMap ; and store it GOTO CHECK_KEY ; and continue to check bit CHECK_COL_34 MOVF kbColumn34_Old,W ; get bit map ( key status ) for keys in column 3 & 4 MOVWF repKeyMap ; and store it GOTO CHECK_KEY ; and continue to check bit CHECK_COL_12 MOVF kbColumn12_Old,W ; get bit map ( key status ) for keys in column 1 & 2 MOVWF repKeyMap ; and store it ;<-------Alt keymap code-------> ;Checks ONLY column 1&2 bitmap ( keymap ) ( as it is now ) ; this code has to be moved/changed if another column is choosen as the alt. keymap toggle key ; alternative keymap handling if key r3 c1 is pressed ( bit 2 in column 1&2 ) ; then enable alternative keymap ( only if keyrepeat is disabled ) ; check if this was the last key pressed ; check bit representing the alt. keymap key ( i've choosen key 2 ) MOVF LastMakeOffset,W ; get key offset again ANDLW H'07' ; mask out column bits SUBLW H'02' ; check if its bit num 2 ( the enter 'alt keymap' key ) BTFSS STATUS, Z ; check if the zero bit is set GOTO CHECK_KEY ; nope than another key was the last ; skip altkeymap enable ; the altkeymap key was the last pressed ! ; is key repeat disabled ? BTFSS I_jmp_NoRepeat ; check if repeat code is enabled ? GOTO CHECK_KEY ; yep, then skip altkeymap enable test ; enable altkeymap if key is still pressed BTFSC repKeyMap,2 ; test bit 2 ( should be key 'F7' ) GOTO CHECK_ENABLE_ALT ; ok enable altkeymap ( if we are not already in altkeymap ) GOTO CHECK_KEY ; nope another key in column 1&2 continue check CHECK_ENABLE_ALT BTFSC _AltKeymap ; are we already in altkeymap ? GOTO CHECK_KEY ; yep then just continue ; We are just entering/enabling the alt. keymap BSF _AltKeymap ; enable alternative keymap ; Example of using an 'advanced' alt keymap handling ; not enabled, to avoid intial confusion. ; I.E This snippet would only be called once when we ; are just entering(enabling) the alternative keymapping ! ; This example code will 'soft' release the current altkeymap key ( it is in pressed state ! ) ; ( i.e send release code for the enter alt. keymap key 'F7' ) ; send the make scancode for left <alt> key instead. ; and force numlock to be off. ; Do not use if you dont understand the implifications ! ; Also note that the scancodes are hardcoded here ! ; ( i.e do not use the lookup table definition of the key/s ) ; ***** start snippet ;MOVLW BREAK ; send break prefix ;CALL ADD_KEY ;MOVLW H'83' ; and scancode for the enter alt keymap ;CALL ADD_KEY ;MOVLW H'11' ; send make code for the left <alt> key ;CALL ADD_KEY ; example of forcing the numlock status to a particular state ; the numlockstatus will change ( be checked ) inside the int routine ; See also at the end of KB_DEBOUNCE_12 where the numlock status ; will be restored when we release the key ;BCF _NumLock ; 'force' numlock to be off ; This bit MUST also be checked as we do not know if we have recevied ; first numlock status byte yet ( pc does not send numlock/led status ; after intial poweron, if not one of the numlock/capslock/scrolllock are pressed ) ; i.e. if you connect this keyboard to a 'running' pc, the numlock status ; will be unknown.However if connected before poweron, it will be updated ; as numlock status is sent during pre-boot seq. ;BTFSC _IsFirstLedStatus ; have we recevied numlock status yet ? ;CALL PRESS_NUMLOCK ; ***** end snippet CHECK_KEY ; 'normal' key down check ; column for pressed key is now in repKeyMap MOVF LastMakeOffset,W ; get offset again ANDLW H'07' ; mask out key number ( lowest 3 bits ) BTFSC STATUS,Z ; bit num zero ? GOTO CHECK_KEY_DONE ; yep lowest bit, check and return MOVWF repTemp ; and store it CHECK_KEY_LOOP RRF repKeyMap,F ; rotate one step to right DECFSZ repTemp,F ; decrement bit counter GOTO CHECK_KEY_LOOP ; loop again CHECK_KEY_DONE ; ok the key to test should now be the lowest bit in repKeyMap CLRC ; clear carry BTFSC repKeyMap,0 ; check bit 0 BSF STATUS,C ; ok key is pressed set carry RETURN ; and we are done.. ; *********************************************************************** ; ; DELAY_1ms - Delay routine ! used when scanning our own keyboard ; Delay is between output of adress to 4051 and reading of inputs ; Increase to have a slower 'scan' rate or decrease to have a higher scan rate. ; DELAY_1ms MOVLW H'F0' ; wait 255 cycles MOVWF kbTemp ; this var is 'safe' to be used in side mainloop MOVLW H'03' MOVWF kbState DELAY_LOOP DECFSZ kbTemp,F ; decrement GOTO $-1 ; MOVLW H'F0' MOVWF kbTemp DECFSZ kbState,F GOTO DELAY_LOOP RETURN ;--------------------------------------------------------------------------- ; ; Initialisation ; ;--------------------------------------------------------------------------- INIT: ;+++ ; Set up the ports ; PORT A BANK1 MOVLW b'00000110' ; Set port data directions RA1,RA2 inputs RA0,RA3,RA4 outputs MOVWF TRISA ; PC keyboard connections ; PORT B ; Used for our own 3x8 matrix keyboard BANK1 MOVLW b'11111000' ; Set port data directions RB4-RB7 inputs rest outputs MOVWF TRISB ; Clear all registers on bank 0 ( memory ) BANK0 MOVLW H'0C' MOVWF FSR INITMEM CLRF 0 ; Clear a register pointed to be FSR INCF FSR,F CLRWDT ; clear watchdog MOVLW H'50' ; Test if at top of memory SUBWF FSR,W BNZ INITMEM ; Loop until all cleared ;+++ ; Initiate the keybuffer pointers INIT_BUFF: MOVLW KbBufferMin ; get adress of first buffer byte MOVWF KeyBufferHead ; store in FSR MOVWF KeyBufferTail ; and set last byte to the same ( no bytes in buffer ) ;+++ ; Preset the timer dividers MOVLW .20 MOVWF Divisor_10ms MOVLW .10 MOVWF Divisor_100ms MOVLW .05 MOVWF Divisor_500ms ;+++ ; Set up Timer 0. ; Set up TMR0 to generate a 0.5ms tick ; Pre scale of /8, post scale of /1 BANK1 MOVLW b'00000010' ; Initialisation of TMR0 prescale 8 '010' ; weak pullup enabled by latch values. MOVWF OPTION_REG ; load option reg with prescale of 8 BANK0 MOVLW H'64' ; start timer with 100 ( 256-100 = 156 ) ; timetick = 0.4uS x 8 ( prescale ) x 156 = (roufly) 0.5 mS MOVWF TMR0 ;--------------------------------------------------------------------------- ; ; the main 'program' loop ( starts really at MAIN_LOOP ) ; ;--------------------------------------------------------------------------- MAIN: BSF pcDATA_in BSF pcCLOCK_in BCF pcDATA_out BCF pcCLOCK_out CLRF PORTB MOVLW H'08' ; preset the column counter MOVWF kbColumnCnt ; BSF _NumLock ; default state is numlock = on BSF _IsFirstLedStatus ; we have not yet recevied led status byte. MOVLW b'10100000' ; enable global & TMR0 interrupts MOVWF INTCON CLRWDT ; clear watchdog BTFSS O_led_KEYCOMM_ok GOTO $-2 ; make an 0.5 second delay here ; i.e. the led will come on when 0.5 seconds has passed ; set inside the timer int. CLRWDT ; clear watchdog BTFSC O_led_KEYCOMM_ok GOTO $-2 ; make an additional 0.5 second delay here ; i.e. the led will be dark when 0.5 seconds has passed ; set inside the timer int. MOVLW H'AA' ; post passed :-), always 0xAA CALL ADD_KEY_BUFFER ; now go into infinite loop, the pc kb interface runs in the background ( as an int ) ; where we continuously monitor the pcCLOCK/DATA_in lines MAIN_LOOP: ; check whatever CLRWDT ; clear watchdog MAIN_CHECK_COL_1: ; scan our own keyboard, first four bits ; address and read column, read as complement so key pressed = '1' ; since we pull down when key is pressed ( weak pullup enabled ) ; ( i.e. pressed key has level 0V ) to make it more 'logical' to work with CLRF kbColumnVal ; get column counter / adress out MOVF kbColumnCnt,W MOVWF PORTB ; set the columns adress to the 74HCT4051 ; i.e. make column low IFNDEF DEBUG CALL DELAY_1ms ; wait 1 ms let pins stabilize ENDIF COMF PORTB,W ; read back the pin values ( complement i.e. key pressed = '1' ) ANDLW b'11110000' ; mask out unused pins MOVWF kbColumnVal ; store the pin values SWAPF kbColumnVal,F ; swap nibbles ( low<->high ) to make room for next column INCF kbColumnCnt,F ; inc column adress MAIN_CHECK_COL_2: ; read next four bits ; put out adress and read next column, read as complement so key pressed = '1' ; this as we pull down when key is pressed ( weak pullup enabled ) ; get column counter / adress out MOVF kbColumnCnt,W MOVWF PORTB ; set the columns adress to the 74HCT4051 ; i.e. make column low IFNDEF DEBUG CALL DELAY_1ms ; wait 1 ms ENDIF COMF PORTB,W ; read back the pin values ( complement i.e. key pressed = '1' ) ANDLW b'11110000' ; mask out unused pins ADDWF kbColumnVal,F ; and store pin values INCF kbColumnCnt,F ; reset column counter check ; i.e. we are 'only' using adress 0 - 7 MOVF kbColumnCnt,W SUBLW H'08' ; subtract value in W with 0x08 BTFSS STATUS, Z ; check if the zero bit is set GOTO MAIN_CHECK_DEBOUNCE ; nope continue CLRF kbColumnCnt ; reset counter/adress MAIN_CHECK_DEBOUNCE: CALL KB_DEBOUNCE ; do debouncing on the current values and send make/break ; for any key that has changed ; NOTE uses the current column adress to determine which ; columns to debounce! MAIN_REPEAT: BTFSS I_jmp_NoRepeat ; check if repeat code is enabled ? GOTO MAIN_CHECK_REPEAT ; yep check key repeating ; keyrepeat disabled then do check on exit of altkeymap instead BTFSS _ExitAltKeymap ; we want to exit altkeymap ? GOTO MAIN_LOOP ; nope ; check that ALL keys are released ; before exiting the alt keymap MOVF kbColumn78_Old,F ; reload column 78 to itself ( affect zero flag ) BTFSS STATUS,Z ; check if zero ? GOTO MAIN_LOOP ; key/s still down in column 78 MOVF kbColumn56_Old,F ; reload column 56 to itself ( affect zero flag ) BTFSS STATUS,Z ; check if zero ? GOTO MAIN_LOOP ; key/s still down in column 56 MOVF kbColumn34_Old,F ; reload column 34 to itself ( affect zero flag ) BTFSS STATUS,Z ; check if zero ? GOTO MAIN_LOOP ; key/s still down in column 34 MOVF kbColumn12_Old,F ; reload column 12 to itself ( affect zero flag ) BTFSS STATUS,Z ; check if zero ? GOTO MAIN_LOOP ; key/s still down in column 12 ; all keys released !! BCF _AltKeymap ; exit altkeymap BCF _ExitAltKeymap ; exit release check BCF _InAltKeymap ; clear flag for second keypress check BCF _DoExitAltKeymap ; GOTO MAIN_LOOP MAIN_CHECK_REPEAT BTFSS _doSendKey ; if we should send a repeated key GOTO MAIN_LOOP ; nope continue ; send the key in RepeatedKey but first check if its an extended key BTFSS _RepeatIsExt ; is it extended ? GOTO MAIN_SEND_REPEAT ; nope just send scan code ; last key pressed was extended send extended prefix MOVLW EXTENDED ; get extended code CALL ADD_KEY_BUFFER ; and put it into the buffer MAIN_SEND_REPEAT: MOVF RepeatKey,W ; get key code for the last pressed key CALL ADD_KEY_BUFFER ; and put it into the buffer BCF _doSendKey ; and clear the flag, it will be set again ; inside int handler if key still is pressed GOTO MAIN_LOOP ; and return ; *********************************************************************** ; ; KB_SEND_KEY - uses the Offset stored in var 'Offset' to fetch a key from our ; key lookup table. ; then checks the bit var _isBreak to see if make or break codes should be sent ; It then puts the code/s into the key buffer ( for sending later, in int routine ) KB_SEND_KEY: MOVF Offset,W ; get current offset MOVWF TempOffset ; save it ( to be used in key repeat code, is its 'make' ) ; temp offset has the following bits: ; '000yyxxx' where 'yy' is column offset ; and 'xxx' is key num, CLRC ; clear carry so it dont affect byte rotation RLF Offset,F ; first rotate RLF Offset,F ; second rotate ; offset no have the following bits: ; '0yyxxx01' where 'yy' is column offset ; and 'xxx' is key num, ; as each key in table has 4 bytes of 'space' INCF Offset,F ; add one, for the 'movwf pcl' at the start of the table BCF Offset,7 ; clear to bit, just in case so we dont ; 'overflow' the table, should not be needed ! BCF _isExtended ; clear extended flag MOVLW LOW LOOKUP_KEY ; get low bit of table adress ADDWF Offset,F ; 8 bit add MOVLW HIGH LOOKUP_KEY ; get high 5 bits BTFSC STATUS,C ; is page boundary crossed ? ADDLW 1 ; yep, then inc high adress MOVWF PCLATH ; load high adress in latch MOVF Offset,W ; load computed offset in w CLRC ; clear carry ( default= key is not extended ) ; if key is extended then carry is set in jumptable lookup_key CALL LOOKUP_KEY ; get key scan code/s for this key ; key scan code/s are saved in ; W - scancode, should go into kbScan ; carry set - extend code ; carry clear - not extended code MOVWF kbScan ; store scancode ; if carry is set then key is extended so first send extended code ; before any make or break code BTFSS STATUS,C ; check carry flag GOTO KB_CHK_BREAK ; nope then check make/break status BSF _isExtended ; set extended flag MOVLW EXTENDED ; CALL ADD_KEY_BUFFER ; get extended code and put in in the buffer KB_CHK_BREAK: ; check if it's make or break BTFSS _isBreak ; check if its pressed or released ? GOTO KB_DO_MAKE_ONLY ; send make code BCF _isBreak ; clear bit for next key ; break code, key is released MOVLW BREAK ; get break code CALL ADD_KEY_BUFFER ; and put into buffer GOTO KB_DO_MAKE ; and send key code also ; key is pressed ! KB_DO_MAKE_ONLY: BCF _doSendKey ; stop repeat sending BCF _doRepeat ; and bit for repeat key send BSF _startRepeat ; and set flag for start key repeat check BCF _RepeatIsExt ; clear repeat key extended flag ( just in case ) BTFSC _isExtended ; is it extended ? BSF _RepeatIsExt ; set the flag ; save this key in 'last' pressed, to be used in key repeat code MOVF TempOffset,W ; get saved offset MOVWF LastMakeOffset ; and store it ; if keyrepat = enabled, alternative mapping = disabled BTFSS I_jmp_NoRepeat ; check if repeat code is enabled ? GOTO KB_REP_NOR ; yep set normal delay ( 800 ms ) ; else keyrepat = disabled, alternative mapping = enabled MOVLW DELAY_ENTER_ALTKEYMAP ;reload delay before entering the altkeymap ( 3 sec ) ; i.e how long the enter altkeymap key must be pressed before ; we enable altkey keymap codes. GOTO KB_REP_SET ; and set it KB_REP_NOR: MOVLW DELAY_REPEAT ; reload 'normal' repeat delay ( 800 ms ) KB_REP_SET: MOVWF RepeatTimer ; and (re)start the timer for key repeat MOVF kbScan,W ; get key scan code MOVWF RepeatKey ; and save it KB_DO_MAKE: ; key pressed/released ( i.e. the scancode is sent both on make and break ) MOVF kbScan,W ; get scan code into w CALL ADD_KEY_BUFFER ; and add to send buffer ; reset the 'get out of alt. keymap timer for each keypress ; note don't care if we are 'in' alt. keymap. Reset this timer anyway ; as the code for checking if we are currently in alt. key map ; would be as long as it takes to reset the timer. MOVLW DELAY_EXIT_ALTKEYMAP ; reload the delay for exiting the altkeymap when no ; key is pressed ( 7.5 sec ) MOVWF LastKeyTime ; (re)set lastkey timer ( used to get out of altkeymap ) RETURN ; *********************************************************************** ; ; KB_DEBOUNCE - debounces two column readings from our keyboard ; If a bit 'state' has been 'stable' for 4 consecutive debounces ; the 'new' byte is updated with the new state ; 'normal' loop time ( no tx/rx/key press ) is about 2-3 ms ; so from 'key' down until 'new' is updated it takes about 8-10 ms ; ( as we are scanning columns two by two, the whole keyboard needs ; 4 loops to be fully updated, then 4 debounce samples for each 'pair' ) KB_DEBOUNCE: ; debounce current column(s) MOVF kbColumnCnt,F ; reload value into itself ( affect zero flag ) BTFSC STATUS,Z ; is it zero ? GOTO KB_DEBOUNCE_78 ; debounce columns 7 & 8 MOVF kbColumnCnt,W ; move column counter into W register SUBLW H'04' ; subtract value in W with 0x04 ( columns 5 & 6 ) BTFSC STATUS, Z ; check if the zero bit is set GOTO KB_DEBOUNCE_34 ; debounce columns 3 & 4 MOVF kbColumnCnt,W ; move column counter into W register SUBLW H'06' ; subtract value in W with 0x02 ( columns 3 & 4 ) BTFSC STATUS, Z ; check if the zero bit is set GOTO KB_DEBOUNCE_56 ; ok column 1 & 2 debounce ; all above tests 'failed' ; columns to debouce are 1 & 2 KB_DEBOUNCE_12: ; debounce columns 1 & 2 DEBOUNCE_BYTE kbColumnVal,kbColumn12_New,kbColumn12Cnt,kbColumn12State MOVF kbColumn12_New,W ; get debounced sample XORWF kbColumn12_Old,W ; get changed bits BTFSC STATUS,Z ; check if zero = no change RETURN ; no change. return ; key/s has been changed, w contains which key/s that has been changed in column 7 & 8 ; Note ! Not the actual state of the key, only 'change has occured' = '1' MOVWF kbState ; save change bit/s MOVLW H'07' ; preset bit counter MOVWF kbBitCnt ; loop though all eight bits. BCF _LastColumn ; clear end seq bit ( set when are done with last bit ) MOVF kbColumn12_New,W ; get new sample MOVWF kbTemp ; and store it KB_LOOP_12 CLRF Offset ; clear offset counter ( for table read ) CLRC ; clear carry RLF kbState,F ; rotate left, and store back BTFSS STATUS,C ; check carry '1' = bit was high = change has occured GOTO KB_LOOP_12_SKIP ; nope, no change check next bit ( or exit ) ; bit changed MOVF kbBitCnt,W ; get bit counter ( for offset calc. ) MOVWF Offset ; store bit num ( for offset ) CLRC ; clear carry RLF kbTemp,F ; rotate left ( next bit ) BTFSS STATUS,C ; check carry '1' = key is down ( i.e. make ) BSF _isBreak ; c = '0' = send break code, i.e. key is released CALL KB_SEND_KEY ; send key code/s make/break uses ; Offset, and _isBreak vars GOTO KB_LOOP_12_NEXT KB_LOOP_12_SKIP RLF kbTemp,F ; rotate so we read next key KB_LOOP_12_NEXT BTFSC _LastColumn ; are we done ? GOTO KB_12_DONE ; yep, save new key bit map and exit DECFSZ kbBitCnt,F ; decrement bit counter GOTO KB_LOOP_12 ; bits left BSF _LastColumn ; set bit so we break out after next run GOTO KB_LOOP_12 KB_12_DONE: ; and update our 'last known' status for the columns MOVF kbColumn12_New,W ; get new status MOVWF kbColumn12_Old ; and store it.. ;<-------Alt keymap code-------> ; ***** alternative keymap handling ; The alternative keymap is enabled by pressing key r4 c1 ( i.e. bit 3 in column 12 ) ; Here, we enable a check to turn off alternative keymap if ; that key and all others are released ( bit is cleared ). ; ( else no (alternative)break codes would be sent for those keys that are still pressed ) ; NOTE: _Altkeymap is set inside int routine when checking ; keyrepeat so there is a 'variable' delay before the altkeymap is active ; BTFSS _AltKeymap ; is altkeymap enabled ? RETURN ; nope return BTFSC _InAltKeymap ; are we in altkeymap ? GOTO KB_12_IN ; yep alt keymap key has been released once ; nope still waiting for first release BTFSS kbColumn12_Old,2 ; is key released ? ( first time ) GOTO KB_12_ALT ; yep, reset timers and set bit variables KB_12_IN BTFSC _DoExitAltKeymap ; are we waiting for release ? GOTO KB_12_OUT ; yes ; the key has been released once test for second press BTFSC kbColumn12_Old,2 ; is it still pressed ? GOTO KB_12_ALT2 ; yep BTFSS _DoExitAltKeymap ; are we now waiting for the last ( second ) release ? RETURN ; nope KB_12_OUT BTFSS kbColumn12_Old,2 ; check if key still pressed ? BSF _ExitAltKeymap ; nope, then enable exit check that ; will exit alt keymap as soon as all key are released KB_12_ALT2 BSF _DoExitAltKeymap ; check for second release RETURN KB_12_ALT ; first release of the enter alt keymap key ; reset 'get out' timer and set bit variables to enable check ; for second press/release MOVLW H'0F' ; x0.5 sec = 7.5 sec MOVWF LastKeyTime ; (re)set lastkey timer ( used to get out of altkeymap automaticly) BSF _InAltKeymap ; yep the first time, then set flag that we are now ; waiting for a second press/release to exit alt key map ; all keys are released before exiting altkeymap ;***** Example snippet(one line) to be paired with code in CHECK_KEY_STATE where I ; forced numlock status to be off while enetering the alt keymap ; but have not yet released the alt keymap toggle key. ; this code will be called at the first release of this key. Used ; to restore numlock status. ; As said before, do not use if implifications are not known ! ;BSF _NumLock ; and also force numlock to be 'on' ; as it is set to 'off' when we enter altkeymap ; we must set it 'back' RETURN KB_DEBOUNCE_34: ; debounce columns 3 & 4 DEBOUNCE_BYTE kbColumnVal,kbColumn34_New,kbColumn34Cnt,kbColumn34State MOVF kbColumn34_New,W ; get debounced sample XORWF kbColumn34_Old,W ; get changed bits BTFSC STATUS,Z ; check if zero = no change RETURN ; no change. return ; key/s has been changed, w contains which key/s that has been changed in column 7 & 8 ; Note ! Not the actual state of the key, only 'change has occured' = '1' MOVWF kbState ; save change bit/s MOVLW H'07' ; preset bit counter MOVWF kbBitCnt ; loop though all eight bits. BCF _LastColumn ; clear end seq bit ( set when are done with last bit ) MOVF kbColumn34_New,W ; get new sample MOVWF kbTemp ; and store it KB_LOOP_34 CLRF Offset ; clear offset counter ( for table read ) CLRC ; clear carry RLF kbState,F ; rotate left, and store back BTFSS STATUS,C ; check carry '1' = bit was high = change has occured GOTO KB_LOOP_34_SKIP ; nope, no change check next bit ( or exit ) ; bit changed MOVF kbBitCnt,W ; get bit counter ( for offset calc. ) MOVWF Offset ; store bit num ( for offset ) BSF Offset,3 ; set bit 3 for table read ( column 3 & 4 ) ;BCF _isBreak ; clear break flag CLRC ; clear carry RLF kbTemp,F ; rotate left ( next bit ) BTFSS STATUS,C ; check carry '1' = key is down ( i.e. make ) BSF _isBreak ; c = '0' = send break code, i.e. key is released CALL KB_SEND_KEY ; send key code/s make/break uses ; Offset, and _isBreak vars GOTO KB_LOOP_34_NEXT KB_LOOP_34_SKIP RLF kbTemp,F ; rotate so we read next key KB_LOOP_34_NEXT BTFSC _LastColumn ; are we done ? GOTO KB_34_DONE ; yep, save new key bit map and exit DECFSZ kbBitCnt,F ; decrement bit counter GOTO KB_LOOP_34 ; bits left BSF _LastColumn ; set bit so we break out after next run GOTO KB_LOOP_34 KB_34_DONE: ; and update our 'last known' status for the columns MOVF kbColumn34_New,W ; get new status MOVWF kbColumn34_Old ; and store it.. RETURN KB_DEBOUNCE_56: ; debounce columns 5 & 6 DEBOUNCE_BYTE kbColumnVal,kbColumn56_New,kbColumn56Cnt,kbColumn56State MOVF kbColumn56_New,W ; get debounced sample XORWF kbColumn56_Old,W ; get changed bits BTFSC STATUS,Z ; check if zero = no change RETURN ; no change. return ; key/s has been changed, w contains which key/s that has been changed in column 7 & 8 ; Note ! Not the actual state of the key, only that 'a change has occured' = '1' MOVWF kbState ; save change bit/s MOVLW H'07' ; preset bit counter MOVWF kbBitCnt ; loop though all eight bits. BCF _LastColumn ; clear end seq bit ( set when are done with last bit ) MOVF kbColumn56_New,W ; get new sample MOVWF kbTemp ; and store it KB_LOOP_56 CLRF Offset ; clear offset counter ( for table read ) CLRC ; clear carry RLF kbState,F ; rotate left, and store back BTFSS STATUS,C ; check carry '1' = bit was high = change has occured GOTO KB_LOOP_56_SKIP ; nope, no change check next bit ( or exit ) ; bit changed MOVF kbBitCnt,W ; get bit counter ( for offset calc. ) MOVWF Offset ; store bit num ( for offset ) BSF Offset,4 ; set bit 4 for table read ( column 5 & 6 ) CLRC ; clear carry RLF kbTemp,F ; rotate left ( next bit ) BTFSS STATUS,C ; check carry '1' = key is down ( i.e. make ) BSF _isBreak ; c = '0' = send break code, i.e. key is released CALL KB_SEND_KEY ; send key code/s make/break uses ; Offset, and _isBreak vars GOTO KB_LOOP_56_NEXT KB_LOOP_56_SKIP RLF kbTemp,F ; rotate so we read next key KB_LOOP_56_NEXT BTFSC _LastColumn ; are we done ? GOTO KB_56_DONE ; yep, save new key bit map and exit DECFSZ kbBitCnt,F ; decrement bit counter GOTO KB_LOOP_56 ; bits left BSF _LastColumn ; set bit so we break out after next run GOTO KB_LOOP_56 KB_56_DONE: ; and update our 'last known' status for the columns MOVF kbColumn56_New,W ; get new status MOVWF kbColumn56_Old ; and store it.. RETURN KB_DEBOUNCE_78: ; debounce columns 7 & 8 DEBOUNCE_BYTE kbColumnVal,kbColumn78_New,kbColumn78Cnt,kbColumn78State MOVF kbColumn78_New,W ; get debounced sample XORWF kbColumn78_Old,W ; get changed bits BTFSC STATUS,Z ; check if zero = no change RETURN ; no change. return ; key/s has been changed, w contains which key/s that has been changed in column 7 & 8 ; Note ! Not the actual state of the key, only 'change has occured' = '1' MOVWF kbState ; save change bit/s MOVLW H'07' ; preset bit counter MOVWF kbBitCnt ; loop though all eight bits. ( 7-0 ) BCF _LastColumn ; clear end seq bit ( set when are done with last bit ) MOVF kbColumn78_New,W ; get new sample MOVWF kbTemp ; and store it KB_LOOP_78 CLRF Offset ; clear offset counter ( for table read ) CLRC ; clear carry RLF kbState,F ; rotate left, and store back BTFSS STATUS,C ; check carry '1' = bit was high = change has occured GOTO KB_LOOP_78_SKIP ; nope, no change check next bit ( or exit ) ; bit changed MOVF kbBitCnt,W ; get bit counter ( for offset calc. ) MOVWF Offset ; store bit num ( for offset ) BSF Offset,4 ; set bit 3,4 for table read ( column 7 & 8 ) BSF Offset,3 ; CLRC ; clear carry RLF kbTemp,F ; rotate left ( next bit ) BTFSS STATUS,C ; check carry '1' = key is down ( i.e. make ) BSF _isBreak ; c = '0' = send break code, i.e. key is released CALL KB_SEND_KEY ; send key code/s make/break uses ; Offset, and _isBreak vars GOTO KB_LOOP_78_NEXT KB_LOOP_78_SKIP RLF kbTemp,F ; rotate so we read next key KB_LOOP_78_NEXT BTFSC _LastColumn ; are we done ? GOTO KB_78_DONE ; yep, save new key bit map and exit DECFSZ kbBitCnt,F ; decrement bit counter GOTO KB_LOOP_78 ; bits left BSF _LastColumn ; set bit so we break out after next run GOTO KB_LOOP_78 KB_78_DONE: ; and update our 'last known' status for the columns MOVF kbColumn78_New,W ; get new status MOVWF kbColumn78_Old ; and store it.. RETURN ; *********************************************************************** ; ; LOOKUP_KEY - lookup table for key scancodes. ; Returns a scancode in w ; Sets carry if key is extended ; NOTE: If key R3 C1 has been pressed longer than keyrepeat delay ; AND keyrepeat is disabled ( jumper on pin RB3 ) THEN the ; bit _AltKeymap is set and we can return an alternative scancode in W ; LOOKUP_KEY ; lookup table for the keys ( 32 keys x 4 lines + 1 = 129 lines ) ; keys are labelled Rx - Cy where x = row number and y = column number ; handles a 4 row x 8 column keyboard = 32 keys MOVWF PCL ; add to program counter ; R1 - C1 i.e. key 1 NOP NOP NOP RETLW H'05' ; scan code 'F1' ; R2 - C1 i.e. key 2 NOP NOP NOP RETLW H'0C' ; scan code 'F4' ; R3 - C1 i.e. key 3 ; The famous alternative keymap toggle key !!! ; It is adviced that this key does not use an alt scancode ; makes things cleaner and simplified. ; IF USED though, remember that a 'soft' release code must be sent ; for this key when entering the altkeymap !( + 'soft' make code for the alternative key ) ; This as the key is pressed when entering altkeymap ; which makes the bit _Altkeymap be set, and hence when released ; the release code for this 'normal' key will never be sent ; instead the release code for the alternative key will be sent. ; To 'fix' this put the release code at the end of CHECK_KEY_STATE ( where the ; alt keymap bit is set ).i.e. break prefix+scancode for normal key. NOP NOP NOP RETLW H'83' ; scan code for F7 ( in 'normal' mode ) ; R4 - C1 i.e. key 4 BTFSC _AltKeymap ; check for alternative keymap RETLW H'76' ; send scancode for 'ESC' instead BSF STATUS,C ; set carry ( i.e. extended code ) RETLW H'6B' ; scan code 'arrow left' '<-' ; R1 - C2 i.e. key 5 NOP NOP NOP RETLW H'06' ; scan code 'F2' hex06 ; R2 - C2 i.e. key 6 NOP NOP NOP RETLW H'03' ; scan code 'F5' ; R3 - C2 i.e. key 7 BTFSC _AltKeymap ; check for alternative keymap RETLW H'0D' ; send scancode for 'horizontaltab' HT instead BSF STATUS,C ; set carry ( i.e. extended code ) RETLW H'75' ; scan code 'arrow up' '^' ; R4 - C2 i.e. key 8 BTFSC _AltKeymap ; check for alternative keymap RETLW H'14' ; send scancode for 'left ctrl' instead BSF STATUS,C ; set carry ( i.e. extended code ) RETLW H'72' ; scan code 'arrow down' ; R1 - C3 i.e. key 9 NOP NOP NOP RETLW H'04' ; scan code 'F3' ; R2 - C3 i.e. key 10 NOP NOP NOP RETLW H'0B' ; scan code 'F6' ; R3 - C3 i.e. key 11 NOP NOP NOP RETLW H'0A' ; scan code 'F8' ; R4 - C3 i.e. key 12 BTFSC _AltKeymap ; check for alternative keymap RETLW H'11' ; send scancode for 'left alt' instead BSF STATUS,C ; set carry ( i.e. extended code ) RETLW H'74' ; scan code 'arrow right' '->' ; R1 - C4 i.e. key 13 BTFSC _AltKeymap ; check for alternative keymap RETLW H'6C' ; send scancode for numeric '7' instead NOP RETLW H'3D' ; scan code '7' ; R2 - C4 i.e. key 14 BTFSC _AltKeymap ; check for alternative keymap RETLW H'6B' ; send scancode for numeric '4' instead NOP RETLW H'25' ; scan code '4' ; R3 - C4 i.e. key 15 BTFSC _AltKeymap ; check for alternative keymap RETLW H'69' ; send scancode for numeric '1' instead NOP RETLW H'16' ; scan code '1' ; R4 - C4 i.e. key 16 BTFSC _AltKeymap ; check for alternative keymap RETLW H'7B' ; send scancode for numeric '-' instead NOP RETLW H'4A' ; scan code '-' minus ( swe kbd ) ; R1 - C5 i.e. key 17 BTFSC _AltKeymap ; check for alternative keymap RETLW H'75' ; send scancode for numeric '8' instead NOP RETLW H'3E' ; scan code '8' ; R2 - C5 i.e. key 18 BTFSC _AltKeymap ; check for alternative keymap RETLW H'73' ; send scancode for numeric '5' instead NOP RETLW H'2E' ; scan code '5' ; R3 - C5 i.e. key 19 BTFSC _AltKeymap ; check for alternative keymap RETLW H'72' ; send scancode for numeric '2' instead NOP RETLW H'1E' ; scan code '2' ; R4 - C5 i.e. key 20 BTFSS _AltKeymap ; check for alternative keymap RETLW H'45' ; scan code '0' ( from keypad ) normal key BSF STATUS,C ; set carry ( i.e. extended code ) RETLW H'1F' ; alt keycode ( windows start menu activate ) ; R1 - C6 i.e. key 21 BTFSC _AltKeymap ; check for alternative keymap RETLW H'7D' ; send scancode for numeric '9' instead NOP RETLW H'46' ; scan code '9' ; R2 - C6 i.e. key 22 BTFSC _AltKeymap ; check for alternative keymap RETLW H'74' ; send scancode for numeric '6' instead NOP RETLW H'36' ; scan code '6' ; R3 - C6 i.e. key 23 BTFSC _AltKeymap ; check for alternative keymap RETLW H'7A' ; send scancode for numeric '3' instead NOP RETLW H'26' ; scan code '3' ; R4 - C6 i.e. key 24 BTFSS _AltKeymap ; check for alternative keymap RETLW H'49' ; scan code '.' ( swe kbd ) normal key ; use alternative keymap BSF STATUS,C ; set carry ( i.e. extended code ) RETLW H'4A' ; send scancode for numeric '/' instead ; R1 - C7 i.e. key 25 BTFSC _AltKeymap ; check for alternative keymap RETLW H'79' ; send scancode for numeric '+' instead NOP RETLW H'4E' ; scan code '+' ; R2 - C7 i.e. key 26 BTFSS _AltKeymap ; check for alternative keymap RETLW H'66' ; scan code 'back space' BS, normal key ; use alternative keymap BSF STATUS,C ; set carry ( i.e. extended code ) RETLW H'71' ; send scancode for 'delete' instead ; R3 - C7 i.e. key 27 BTFSS _AltKeymap ; check for alternative keymap RETLW H'5A' ; scan code 'enter', normal key ; use alternative keymap BSF STATUS,C ; set carry ( i.e. extended code ) RETLW H'5A' ; send scancode for numeric enter instead ( note ! extended ) ; R4 - C7 i.e. key 28 BTFSS _AltKeymap ; check for alternative keymap RETLW H'5A' ; scan code 'enter', normal key ; use alternative keymap BSF STATUS,C ; set carry ( i.e. extended code ) RETLW H'5A' ; send scancode for numeric enter instead ( note ! extended ) ; R1 - C8 i.e. key 29 NOP NOP NOP RETLW H'2C' ; scan code 't' ; R2 - C8 i.e. key 30 NOP NOP NOP RETLW H'24' ; scan code 'e' ; R3 - C8 i.e. key 31 NOP NOP NOP RETLW H'1B' ; scan code 's' ; R4 - C8 i.e. key 32 NOP NOP NOP RETLW H'2C' ; scan code 't' END
  19. Boa tarde. Estou começando a utilizar meu pic 16f15376 (ganhei em uma promoção da microchip há muito tempo). Eles criaram o módulo de programação "em nuvem" e estou tentando aprender a utilizar cada periférico do PIC. Tenho um display SSD1306 128x32 e mesmo sabendo qual o endereço, quis fazer uma função genérica para que o PIC encontre o endereço correto. Outro detalhe é que estou tentando utilizar a própria biblioteca i2c gerada. Mas, infelizmente não tive sucesso. Disponibilizei todo o código que utilizo, caso alguém possa ajudar, agradeço. Abaixo, encontra-se uma tela com o resultado do programa. Ele executa até a linha de printf e quando manda a função i2c1write....trava. mcc_generated_files.rar
  20. Olá pessoal este tópico ,é para falar sobre cálculos de transformadores de ferrites ,dúvidas , sugestão, criação de trafos experimentais etc... Qual seria a melhor fonte os grandes de ferro silício ou os superpermalloy e os de ferrites ? Fiquem a vontade pessoal! Outra coisa eu gostaria de programa-lo e medir sua temperatura ,corrente e tensão! Aceito sugestões de códigos em CCS ou em C mesmo ! Thanks ,
  21. ola pessoal ja sei o que é prescaler e postscaler só falta entender o que o PR2 do timer2 do pic faz para poder me transformar em super sayajin kkkk pr2.bmp
  22. Pessoal, Estou procurando um exemplo de código em C CCS para transmitir protocolo DMX512 para equipamento de iluminação via PIC. Minha intenção é montar uma mini mesa DMX. Encontrei um tópico aqui no fórum (Decodificando protocolo DMX 512) onde o Ricardo S Ferreira diz que tem esse exemplo, mas não consigo enviar mensagem p ele. Um esquema da eletrônica também vai ajudar bastante. Obrigado
  23. Olá, Eu gostaria de fazer um projeto baseado em um microcontrolador PIC16F: o projeto é projetar um ESC sem sensor para motores CC sem escova. O algoritmo sem sensor baseia-se na detecção do evento cruzado zero do EMF de volta que ocorre 30 graus elétricos antes que a próxima comutação ocorra. O PWM com uma frequência de 20 kHz é usado para variar a tensão efetiva aplicada ao motor, de modo a controlar a velocidade. Como o PWM pode ser muito barulhento, o EMF de retorno na fase flutuante deve ser filtrado com um filtro passa-baixo para que o evento cruzado zero seja detectado com precisão, os comparadores também são usados para comparar a tensão de EMF de retorno com a tensão de ponto neutro virtual. Portanto, para filtrar o EMF de volta, são usados filtros passa-baixa RC, o problema é que esses filtros introduzem um atraso no sinal filtrado, e esse atraso deve ser levado em consideração para calcular o tempo antes do ponto de comutação. Encontrei na internet um tutorial muito bom que explica como funcionam os filtros passa-baixas RC e algumas fórmulas para calcular a reatância capacitiva e escolher os valores do capacitor e do resistor, para filtrar um sinal que possui uma frequência conhecida, além de fornecer também uma fórmula para calcular o atraso da fase em graus, mas não fornece uma fórmula para calcular o atraso do tempo em segundos ou µs ... aqui o link deste tutorial: Alguém pode me ajudar a escolher os valores corretos de capacitor e resistor e também fornecer etapas detalhadas para calcular o atraso de tempo para qualquer valor de capacitor e resistor? Obrigado
  24. Quero deixar um bom conteúdo , e aproveitar para ganhar mais conhecimento TIMER2 é um temporizador de 8 bits com um divisor( prescaler ) e um postscaler. Ele pode ser usado como a base de tempo de PWM para o modo de PWM do módulo CCP. O registo TMR2 é legível e gravável, e é eliminada em qualquer redefinição do dispositivo. O relógio de entrada (FOSC / 4) tem uma opção de pré-escala de 1:1, 1:4, ou 1:16, selecionados por bits de controle T2CKPS1: T2CKPS0 (T2CON.01 : 00). O módulo Timer2 tem um registo de 8 bits: PR2. TMR2 incrementos de 00h até que seu valor seja igua a PR2, depois reinicia a 00h. PR2 é um registo de leitura e escrita. A cada n comparações, será gerada uma interrução. n é definido pelo Potscaler(1:1 - 1:16). Quer dizer que se Potscaler for igual a 1:4, depois de 4 comparações (TMR2=PR2) será gerada uma interrupção adicionado 9 minutos depois Questão : Suponha que eu tenha "Fosc" como minha frequência do MCU e precise usar o módulo Timer2 para gerar uma interrupção em um determinado momento "t". Como devo calcular o valor PR2 necessário? Existe uma equação que eu possa usar? Não consigo ter uma ideia de como calculá-lo na folha de dados pic16f1937. Qualquer ajuda vai fazer, obrigado! Rsrs! adicionado 14 minutos depois Resolução: rs você pode derivar o valor PR via FOSC, a frequência desejada para o temporizador e os valores da escala Pré e Pós. O PR2 será incrementado em cada extremidade do relógio do temporizador (neste caso, é o FOSC / 4). Você usa os bits de pré-escala para selecionar quantas dessas bordas do relógio devem ser detectadas para gerar uma única contagem do registro TMR. Os bits de escala de escala selecionam quantas vezes o TMR2 deve corresponder ao PR2 para gerar uma interrupção do Timer2. Fdesired = Frequência alvo em Hz Tdesired = (1 / Fired) Finput = (Fosc / 4) Tinput = 1 / (Finput) PR = Tired / (Tinput * Prescale * Pós-escala) Digamos, por exemplo, que você queira um temporizador de 1KHz a partir de 8MHz Fosc: desejado = 1000Hz Tdesired = (1 / 1000Hz) = 0.001s Finput = (8000000Hz / 4) = 2000000Hz Tinput = (1 / 2000000Hz) = 0.0000005s Pré- escala = 4 Pós-escala = 1 PR = 0.001 / (0.0000005 * 4 * 1) = 500 No entanto, PR2 é um registro de 8 bits, então nós precisa diminuir esse valor de PR , não há nenhuma 1: 8 prescalar, então em vez disso, pode definir o pré-escala a 4 e o Postscale a 2 para diminuir PR: PR = 0,001 / (0,0000005 * 4 * 2) = 250 , que encaixam no registro de 8 bits.
  25. Achei esta página na net. Alguém pode conferir se ela calcula correto? Calcular Timer CCS

Sobre o Clube do Hardware

No ar desde 1996, o Clube do Hardware é uma das maiores, mais antigas e mais respeitadas publicações sobre tecnologia do Brasil. Leia mais

Direitos autorais

Não permitimos a cópia ou reprodução do conteúdo do nosso site, fórum, newsletters e redes sociais, mesmo citando-se a fonte. Leia mais

×
×
  • Criar novo...

Aprenda_a_Ler_Resistores_e_Capacitores-capa-3d-newsletter.jpg

ebook grátis "Aprenda a ler resistores e capacitores", de Gabriel Torres

GRÁTIS! BAIXE AGORA MESMO!