Post on 25-Oct-2020
Interfaceando Microcontroladores
Daniel Quadrosdqsoft.blogspot.com
dqsoft.blogspot@gmail.com
O Palestrante
• Engenheiro Eletrônico 81• Scopus Tecnologia (82-86)• Humana Informática (86-93)• Seal Eletrônica (93-02)• Tamid Tecnologia (02-)
O Blog
Parabéns pelo seu blog, está muito chique.Abraço e sucesso! – Sóda Games
Daniel, muito bom este artigo! Estou ansioso para ler os próximos! Parabéns – Breno Faria
grande post. sanou minhas duvidas a respeito de filas e pilhas - José Victor
Quanta boçalidade e verborragia junta. - Anônimo
A Palestra
• Programação de periféricos internos• E/S Digital: LEDs, Teclas, Buzzer e outros• E/S Analógica: ADC e DAC• Comunicação Serial Assíncrona• Comunicação a 2 fios: I2C• Comunicação a 3 fios• Comunicação a 4 fios: SPI e Microwire• Interface c/ LCD de múltiplos segmentos• Interface c/ LCD alfanumérico
Exemplos
• Escolhidos por– Disponibilidade– Variedade
• Não necessariamente os melhores
Exemplos – PIC 8bits
• Largamente em uso• Disponível em encapsulamento DIP
Exemplos – PIC 8bits
RISCArquitetura Harvard
Exemplos – PIC 8bits
• Inapropriado para C– Constantes x Variáveis, Pilha
• Compilador C CCS v4.023• Modelo usado: PIC16F628A, PIC12F675/629
McLab 1 da Mosaico
Exemplos – HCS08
• Ancestrais famosos (6800, 6502, 6809)• Várias sub-famílias
Exemplos – HCS08
• Arquitetura von Neumann• ULA de 8bits / Multiplicação• CISC• Muitos modos de endereçamento
Exemplos – HCS08
• Compilador Metrowerks CodeWarrior• Modelo usado: MC9S08LL16• Placa DEMO9S08LL16 (Obrigado, Freescale!)
Exemplos – MSP430
• Popular em sistemas de baixo consumo• Poucos modelos DIP
Exemplos – MSP430
• Arquitetura von Neumann• ULA e Registradores de 16 bits• Espaço de endereçamento único• CISC, altamente ortogonal
Memória
Registradores
Exemplos – MSP430
• Boa arquitetura p/ C• Compilador C IAR v4.20• Modelo usado: MSP430F201x
(Obrigado, Texas)
Exemplos - Periféricos
• Relógio + Ram– PCF8583– DS1002
• EEProm– 24W256– 93C66
• LEDs• Display 7 segmentos• Buzzers• Servo motor• LCD alfanumérico
Periféricos Internos
• Recursos internos aos microcontrolador• Configurados e controlados por registradores
Periféricos Internos
• Mapeados em memória
Periféricos Internos
• Metrowerk e IAR: recursos padrões do C• CCS PIC C
– Não permite manipular diretamente bits dos registradores
– “funções” intrínsecas para configurar periféricos (geram código inline)
12F629.h: #define PIN_A1 41Main.c: output_high(PIN_A1);
Periféricos Internos
typedef union { byte Byte; struct { byte PTAD0 :1; byte PTAD1 :1; byte PTAD2 :1; byte PTAD3 :1; byte PTAD4 :1; byte PTAD5 :1; byte PTAD6 :1; byte PTAD7 :1; } Bits;} PTADSTR;extern volatile PTADSTR _PTAD @0x00000000;
#define PTAD _PTAD.Byte#define PTAD_PTAD1 _PTAD.Bits.PTAD1#define PTAD_PTAD2 _PTAD.Bits.PTAD2#define PTAD_PTAD3 _PTAD.Bits.PTAD3#define PTAD_PTAD4 _PTAD.Bits.PTAD4#define PTAD_PTAD0_MASK 1#define PTAD_PTAD1_MASK 2#define PTAD_PTAD2_MASK 4#define PTAD_PTAD3_MASK 8
CodeWarriormc9s08ll16.h
Periféricos Internos
__no_init volatile union{ unsigned char BCSCTL1; struct { unsigned char RSEL0 : 1; unsigned char RSEL1 : 1; unsigned char RSEL2 : 1; unsigned char RSEL3 : 1; unsigned char DIVA0 : 1; unsigned char DIVA1 : 1; unsigned char XTS : 1; unsigned char XT2OFF : 1; } BCSCTL1_bit; } @ 0x0057;
enum { RSEL0 = 0x0001, RSEL1 = 0x0002, RSEL2 = 0x0004, RSEL3 = 0x0008, DIVA0 = 0x0010, DIVA1 = 0x0020, XTS = 0x0040, XT2OFF = 0x0080,};
IARio430x20x3.h
Periféricos Internos
• Bits individuais acessados diretamente
if (PTAD_PTAD7 == 0) { while(PTAD_PTAD7 == 0) ; PTAD_PTAD1 = 1;}
• É comum utilizar manipulação de bits
BCSCTL1 = XT2OFF + DIVA1 + XTS;BCSCTL1 = BCSCTL1 | XT2OFF;BCSCTL1 = BCSCTL1 & ~XT2OFF;BCSCTL1 = (BCSCTL1 & ~DIVA) | (DIVA1 + DIVA0)
Interrupções• Interrompe momentaneamente o fluxo normal
de execução• Vantagens:
– Resposta rápida (latência)– Simplifica a execução normal
• Desvantagens– Causa corridas críticas e re-entrâncias– Consome pilha– Consome tempo (overhead)
Bibliotecas dos Fabricantes
• Não reinventar a roda. Mas...• Fabricantes de HW não de SW• Verificar se é apropriada
– Consumo de Ram e Flash– Uso de outros recursos– Desempenho– Impacto em watchdog e interrupções
E/S Digital
• A maioria dos pinos dos micro-controladores pode ser usada para isto
• Entrada digital: tensão no pino é lida como 0 ou 1
• Saída digital: tensão no pino é controlada pelo software entre os níveis “alto” e “baixo”
• Pode ser usada para implementar outras interfaces
• Recursos adicionais– Pull-up / pull-down interno– Interrupção na mudança– Schmitt trigger
E/S Digital
Obs.: GP3 somente entrada
Tecla e LED - PIC
E/S Digital
// Includes e definições#include <12F675.h>#device adc=8#use delay(clock=4000000)#fuses NOWDT, NOCPD, NOPROTECT#fuses NOMCLR#fuses INTRC_IO, PUT, BROWNOUT// Pinos utilizados#define BOTAO PIN_A5#define LED PIN_A4
void main(){ set_tris_a (0xEF); #use fast_io(A) port_a_pullups(0x20); setup_timer_0(RTCC_INTERNAL|RTCC_DIV_1); setup_timer_1(T1_DISABLED); setup_comparator(NC_NC); setup_vref(FALSE); setup_adc(ADC_OFF); for (;;) { if (input(BOTAO) == 0) output_high(lEd); else output_low(LED); }}
• CCS PIC C– #use fast_io(port)
• Código gerado não altera direção dos pinos• Programador deve usar set_tris_X• Melhor opção se direções não se alteram
– #use fixed_io (port_outputs=pin, pin, …)• Programa a direção do pino a cada acesso• Direções definidas no pragma
– #use standard_io (port)• Programa a direção do pino a cada acesso• Direção definida pela instrução de acesso• Default
E/S Digital
E/S Digital
#include <MC9S08LL16.h>#define BOTAO_APERTADO() (PTAD_PTAD6==0)#define ACENDE_LED() PTBD_PTBD7 = 0#define APAGA_LED() PTBD_PTBD7 = 1void main(){ SOPT1 = 0x63; // Desliga COP, // liga STOP,BKGD,RESET SPMSC1 = 0x00; // Desliga LVD PTADD = 0x3F; // A6 e A7 entradas PTBDD = 0xFF; // todos saída PTCDD = 0x7F; // D7 entrada PTDDD = 0xFF; // todos saída PTEDD = 0xFF; // todos saída
Tecla e LED – HCS08
PTAPE = 0xC0; // pullups PTAD = 0x00; PTBD = 0xF0; // LEDs off PTCD = 0x3C; // LEDs off PTDD = 0x00; PTED = 0x00; for (;;) { if (BOTAO_APERTADO()) ACENDE_LED(); else APAGA_LED(); }}
E/S Digital
Tecla e LED – MSP430
E/S Digital#include "io430.h"#define BOTAO 0x01#define LED 0x04int main( void ){ WDTCTL = WDTPW + WDTSSEL + WDTHOLD; P1SEL = 0; // Todos os pinos como I/O P1DIR = 0xFF & ~BOTAO; // Somente o botão é entrada P1REN = BOTAO; // Usar resistor no botão P1OUT = BOTAO; // Resistor é pullup BCSCTL1 = CALBC1_12MHZ; // Calibração de fábrica DCOCTL = CALDCO_12MHZ; // Ativa DCO 12MHz while (1) { if ((P1IN & BOTAO) == 0) P1OUT |= LED; else P1OUT &= ~LED; }}
E/S Digital - LEDs
• Organizar como matriz• Multiplexação: Seleciona
uma linha por vez• Decoders n2n
• Shift registers• Drivers específicos
Controlando muitos LEDs
E/S Digital - LEDs
8 dígitos de 8 segmentos = 64 segmentos
E/S Digital - LEDs
E/S Digital - LEDs
static void VarreDisp (void){ static unsigned int8 disp = 0; unsigned int8 i, mask; // apaga os segmentos output_low (SHIFT_MR); delay_us (DLY_SR); output_high (SHIFT_MR); output_high (SHIFT_ST); delay_us (DLY_SR); output_low (SHIFT_ST);
// seleciona o display atual if (disp & 1) output_high (SEL_0); else output_low (SEL_0); if (disp & 2) output_high (SEL_1); else output_low (SEL_1); if (disp & 4) output_high (SEL_2); else output_low (SEL_2);
PIC
E/S Digital - LEDs
// acende os segmentosfor (i=0, mask=0x80; i<8; i++){ if (segto[disp] & mask) output_high (SHIFT_DS); else output_low (SHIFT_DS); delay_us (DLY_SR); output_high (SHIFT_SH); delay_us (DLY_SR); output_low (SHIFT_SH); delay_us (DLY_SR); mask = mask >> 1;}
output_high (SHIFT_ST); delay_us (DLY_SR); output_low (SHIFT_ST); // passa para o seguinte disp = (disp + 1) & 7;}
E/S Digital - LEDs
Peggy 2
Meggy Jr
Mignonette
http://www.evilmadscientist.com/
E/S Digital - PWM
• Implementação– Hardware– Interrupção– Loops
• Usos– Intensidade LEDs– Gerar sons– Controle de Servo
E/S Digital - Servomotor
E/S Digital - Servomotor
E/S Digital - Servomotor
#int_RTCCvoid RTCC_isr() { SET_TIMER0 (192); cnt_periodo--; if (cnt_periodo == 0) { cnt_periodo = TMP_PERIODO/TMP_OVF; cnt_duty = duty; output_high (MOTOR); } else if (cnt_duty != 0) { cnt_duty--; if (cnt_duty == 0) output_low (MOTOR); }}
E/S Digital -Dimmer
byte duty = 0;setup_ccp1(CCP_PWM);setup_timer_2(T2_DIV_BY_1,128,1);set_pwm1_duty(duty);for (;;){ if (input(PIN_A1) == 0) { duty = (duty + 16) & 0x7F; set_pwm1_duty(duty); delay_ms (300); }}
E/S Digital - Buzzers
• Capsula Piezoelétrica– Emite som quando tensão varia– Microcontrolador gera frequência
• Buzzer– Capsula Piezoelétrica + oscilador– Emite som quando recebe tensão– Microcontrolador modula a frequência
E/S Digital - Buzzers
HCS08
E/S Digital - Buzzers#define ON 1#define OFF 0unsigned char beep = OFF;ICSC2 = 0; // full speedTPM1SC = 0x08; // clock = bus clockfor(;;) { if (BOTAO_APERTADO()) { if (beep == OFF) { TPM1CNTH = 0; // resseta contagem TPM1C0SC = 0x28; // edge aligned PWM TPM1MOD = 3200; // periodo = 2.5 KHz TPM1C0V = 1600; // onda quadrada beep = ON; } } else { if (beep == ON) { TPM1C0SC = 0x00; beep = OFF; } }}
E/S Digital - Teclas
• Debounce• Matriz de teclas• Varrer linhas, ler
colunas• Shift, Trava• Auto-repetição
E/S Digital - Teclado
MSP430 #define TEC_L1 0x01 // P1.0#define TEC_L2 0x02 // P1.1#define TEC_C1 0x04 // P1.2#define TEC_C2 0x08 // P1.3#define TEC_C3 0x10 // P1.4#define TEC_LIN (TEC_L1 | TEC_L2)#define TEC_COL (TEC_C1 | TEC_C2 | TEC_C3)#define N_LIN 2#define N_COL 3const byte mskLin[N_LIN] = {TEC_L1,TEC_L2};const byte mskCol[N_COL] = { TEC_C1, TEC_C2, TEC_C3 };const byte codTec [2][N_LIN][N_COL] ={ 0xF1, '1', '2', '3', '4', '5', 0xF1, 'A', 'B', 'C', 'D', 'E'};
E/S Digital - Teclado
if (tecEstado == APERTADA) { P1OUT &= ~TEC_LIN; if ((P1IN & TEC_COL) == TEC_COL) tecEstado = SOLTA; P1OUT |= TEC_LIN;}else { // ...........................}
for (lin = 0; lin < N_LIN; lin++) { P1OUT &= ~mskLin[lin]; tec = P1IN; P1OUT |= mskLin[lin]; for (col = 0; col < N_COL; col++) if ((tec & mskCol[col]) == 0) break; if (col < N_COL) { tec = codTec[shftEstado][lin][col]; if (tec == 0xF1) shftEstado = !shftEstado; else { col = (poeTec+1) & 7; if (col != tiraTec) { filaTec[poeTec] = tec; poeTec = col; } } }}
// estado do tecladoenum {SOLTA, APERTADA} tecEstado;byte shftEstado;// fila do tecladobyte filaTec[8];int poeTec, tiraTec;
E/S Digital – 3V x 5V
• Examinar as especificações , cuidado c/ tempos• Saída de 5V ligada a entrada de 3V
– Divisor resistivo (direto ou via transistor)– OpAmp, ULN2003– Abusar da proteção interna
• Saída de 3V ligada a entrada de 5V– Ligar direto– Transistor, ULN2003, OpAmp
• Conexão Bi-direcional– OpAmp– Conversor dedicado
Fonte: SLAA148
Entradas Analógica - ADC
• Converte tensão em valor binário• Necessário adequar tensão às referências
disponíveis no µControlador• Entradas multiplexadas• Conversão não é instantânea• Técnicas mais usadas (hw):
– Aproximação sucessiva– Sigma Delta
Entradas Analógica - ADC
ADCCFG = 0x60; // 8bit, clock=bus clock/8APCTL1_ADPC6 = 1; // habilita ADPC6ADCSC1 = 0x06; // leitura única, ADC6while (!(ADCSC1 & 0x80)) ;leitura = ADCRL;
HCS08
Entradas Analógica - ADC
MSP430
Entradas Analógica - ADC
// 1.2V ref, SMCLKSD16CTL = SD16VMIDON + SD16REFON + SD16DIV_0 + SD16SSEL_1; // PGA = 1x, Diff inputs A3- & A3+SD16INCTL0 = SD16INCH_3; // Single conversion, Unipolar, 256 SR, Int enableSD16CCTL0 = SD16SNGL + SD16UNI + SD16IE; // VMID off: used to settle ref capSD16CTL &= ~SD16VMIDON; // P1.6 & P1.7: A3+/- SD16_A inputsSD16AE = SD16AE6 + SD16AE7;
if (!bAmostrando){ SD16CTL |= SD16REFON; // Liga a referência do SD16 SD16CCTL0 |= SD16SC; // Inicia uma conversão bAmostrando = TRUE; _BIC_SR_IRQ (SCG1+SCG0); // Manter osciladores ligados}
Entradas Analógica - ADC// Limites para detectar passagem #define VREPOUSO 88#define VPOS_1 120#define VPOS_2 100#define VNEG_1 56#define VNEG_2 76
static byte bAmostrando = FALSE;static byte bDetectou = FALSE;
#pragma vector = SD16_VECTOR__interrupt void SD16ISR(void){ unsigned int medida; SD16CTL &= ~SD16REFON; // Desliga referência do SD16_A medida = SD16MEM0 >> 8; // Pega medida (limpa IFG) bAmostrando = FALSE; // Fim da amostragem if (!bDetectou && ((medida > VPOS_1) || (medida < VNEG_1))) bDetectou = TRUE; else if (bDetectou && (medida < VPOS_2) && (medida > VNEG_2)) bDetectou = FALSE; _BIS_SR_IRQ (SCG1+SCG0); // voltar para LPM3}
Saída Analógica - DAC
• Gera tensão proporcional a valor binário• Periférico menos comum
– Não disponível nos meus exemplos
• Opções externas– Conversor dedicado– Rede de resistores
Comunic. Serial Assincrona
• Start pode vir a qualquer instante• Demais bits determinados pela taxa• Stop e paridade podem ser usados para
consistência
Comunic. Serial Assincrona
• Implementação por Software– Requer precisão no tempo dos bits– Determinar start na recepção pode ser problemático
• Implementação por Hardware– Shift registers– Interrompe qdo recebe ou pronto p/ tx– Cuidado com overrun– Opções com buffer maior
Comunic. Serial Assincrona
• RS-232– Transmissor deve operar com ±12V– Receptor deve aceitar ± 3 a 15V– Dados: 1 = -12V, 0 = +12V– Sinais de controle: ativo = +12V, inativo = -12V
• RS-422, RS-485, etc
Comunic. Serial Assincrona
byte bufTx;volatile int8 bitTx = -1;void TxByte (byte b){ while (bitTx != -1) ; bufTx = b; bitTx = 0;}
void RTCC_isr() { SET_TIMER0 (76); if (bitTx == 10) bitTx = -1; if (bitTx != -1) { if (bitTx == 0) output_low (SERIAL_TX); else if (bitTx == 9) output_high (SERIAL_TX); else { if (bufTx & 1) output_high (SERIAL_TX); else output_low (SERIAL_TX); bufTx = bufTx >> 1; } bitTx++; }}
PIC Tx“bit banging”
Comunic. Serial Assincrona
volatile int8 bitRx;#INT_RAvoid RA_isr(){ if ((input(SERIAL_RX)==0) && (bitRx==0)) { bitRx = 1; disable_interrupts(INT_RA4); SET_TIMER1 (0xFFFF - 60); disable_interrupts(INT_RTCC); enable_interrupts(INT_TIMER1); }}
PIC Rx
Comunic. Serial Assincrona
byte filaRx [16];volatile int8 poeRx, tiraRx;byte bufRx;#INT_TIMER1void TrataRx (){ int1 dado; int8 aux; SET_TIMER1 (0xFFFF-156); dado = input (SERIAL_RX); switch (bitRx) { // ..................... }}
case 1: // confirma start bitRx = 2; break;case 10: // confirma stop if (dado == 1) { aux = (poeRx+1) & 0xF; if (aux != tiraRx) { filaRx[poeRx] = bufRx; poeRx = aux; } } enable_interrupts(INT_RA4); disable_interrupts(INT_TIMER1); enable_interrupts(INT_RTCC); break;default: // 2 a 9 = dados bufRx = bufRx >> 1; if (dado == 1) bufRx |= 0x80; bitRx++; break;
PIC Rx(cont)
Comunic. Serial Assincrona
HCS08#define BusClock 8000000#define Baud 19200void InitSCI () { ICSC1 = 0x04; ICSC2 = 0x00; ICSSC = NVFTRIM; ICSTRM = NVICSTRM; while (ICSC1_CLKS != ICSSC_CLKST) ; SCIBD = BusClock/16/Baud; SCIC1 = 0; // 8N1 SCIC2 = 0x2C; // Int Rx, Rx e Tx}
SCIS1
SCIC1
SCIC2
Comunic. Serial Assincrona
int LeRx () { int ret = -1; DisableInterrupts; if (poeFilaRx != tiraFilaRx) { ret = filaRx[tiraFilaRx]; tiraFilaRx = (tiraFilaRx+1)&7; } EnableInterrupts; return ret;}void TxByte (byte c) { while(!SCIS1_TDRE) ; SCID = c;}
static byte filaRx[8];static volatile int poeFilaRx = 0;Static volatile int tiraFilaRx = 0;interrupt VectorNumber_Vscirx void SCIRx_ISR(void) { if (SCIS1_RDRF) { byte c = SCID; int prox = (poeFilaRx + 1) & 7; if (prox != tiraFilaRx) { filaRx[poeFilaRx] = c; poeFilaRx = prox; } }}
Comunicação 2 Fios - I2C
• SCL: Clock gerado pelo mestre• SDA: Dado/Endereço (bidirecional)• Endereços de 7 bits • Pinos de endereçamento para ligar “em varal”
dispositivos do mesmo tipo
Comunicação 2 Fios - I2C
Normalmente SDA muda somente quando SCL = 0
Comunicação 2 Fios - I2C
Leitura EEProm
Escrita EEProm
Comunicação 2 Fios - I2C
Comunicação 2 Fios - I2C
• PCF8583 (Relógio + Ram)– Relógio: 12/24 horas– Calendário: 4 anos– Alarme: data + horário– 240 bytes de Ram– Baixo consumo– Uso com cristal de 32KHz– Endereço I2C: 1 0 1 0 0 0 A0– I2C 100KHz
Comunicação 2 Fios - I2C
#define sda_alto output_float(PCF8583_SDA)#define sda_baixo output_low(PCF8583_SDA)#define scl_alto output_float(PCF8583_SCL)#define scl_baixo output_low(PCF8583_SCL)byte PCF8583_TxByte (byte b){ int i; byte ack; for (i = 0; i < 8; i++) { if (b & 0x80) sda_alto; else sda_baixo; delay_us (5); scl_alto; delay_us (5); scl_baixo; b = b << 1; } delay_us (5); scl_alto; ack = input(PCF8583_SDA) == 0; delay_us (5); scl_baixo; return ack;}
PIC
Comunicação 2 Fios - I2C
void PCF8583_Write (byte ender, byte dado){ // Start delay_us (10); sda_baixo; delay_us (5); scl_baixo; delay_us (5); // Efetua a escrita if (PCF8583_TxByte (PCF8583_ADDR) && PCF8583_TxByte (ender) && PCF8583_TxByte (dado)) ; // Stop delay_us (5); sda_baixo; delay_us (5); scl_alto; delay_us (5); sda_alto;}
Comunicação 2 Fios - I2C
byte PCF8583_Read (byte ender){ byte result = 0; delay_us (10); sda_baixo; delay_us (5); scl_baixo; delay_us (5); if (PCF8583_TxByte (PCF8583_ADDR) && PCF8583_TxByte (ender)) { delay_us (10); scl_alto; delay_us (10); sda_baixo; delay_us (5); scl_baixo; delay_us (5); if (PCF8583_TxByte(PCF8583_ADDR|1)) result = PCF8583_RxByte (); } delay_us (5); sda_baixo; delay_us (5); scl_alto; delay_us (5); sda_alto; return result;}
byte PCF8583_RxByte (){ int i; byte dado; for (i = 0; i < 8; i++) { delay_us (5); scl_alto; dado = dado << 1; if (input(PCF8583_SDA)) dado |= 1; delay_us (5); scl_baixo; } sda_alto; delay_us (5); scl_alto; delay_us (5); scl_baixo; return dado;}
Comunicação 2 Fios - I2C
• 24WC256 – EEProm 256Kbits (32Kx8)– Não volátil (100 anos)– 100.000 ciclos de apagamento– I2C 1MHz– Tempo de escrita: 5ms (Vcc 3 a 5,5V)– Page Write (Page = 64 bytes)– Endereço I2C : 1 0 1 0 0 A1 A0 R
Comunicação 2 Fios - I2C
MSP430// Iniciação USIUSICKCTL = USIDIV2 | USIDIV0 | USISSEL1 | USICKPL | USISWRST;USICTL0 = USIPE7 | USIPE6 | USIMST;USICTL1 = USII2C;USICNT = 0;USICKCTL &= ~USISWRST;// Transmite um byte byte I2C_TxByte (byte b){ USISRL = b; USICTL0 |= USIOE; USICNT = 8; while ((USICTL1 & USIIFG) == 0) ; USICTL0 &= ~USIOE; USICNT = 1; while ((USICTL1 & USIIFG) == 0) ; return (USISRL & 1) == 0;}
USI• I2C e SPI• Shift & Clock• Interrupções
Comunicação 2 Fios - I2C
// Recebe um bytebyte I2C_RxByte (){ byte dado; USICTL0 &= ~USIOE; USICNT = 8; while ((USICTL1 & USIIFG) == 0) ; dado = USISRL; USISRL = 0xFF; USICTL0 |= USIOE; USICNT = 1; while ((USICTL1 & USIIFG) == 0) ; return dado;}
void I2C_TxStart (){ USICTL0 |= USIOE; USISRL = 0xFF; USICNT = 1; while ((USICTL1 & USIIFG) == 0) ; USISRL = 0; USICTL0 |= USIGE; USICTL0 &= ~USIGE;}void I2C_TxStop (){ USICTL0 |= USIOE; USISRL = 0; USICNT = 1; while ((USICTL1 & USIIFG) == 0) ; USISRL = 0xFF; USICTL0 |= USIGE; USICTL0 &= ~(USIGE | USIOE);}
Comunicação 2 Fios - I2C
void EEProm_Write (word ender, byte dado){ I2C_TxStart(); I2C_TxByte (ADDR); I2C_TxByte ((byte) (ender >> 8)); I2C_TxByte ((byte) (ender & 0xFF)); I2C_TxByte (dado); I2C_TxStop();}
byte EEProm_Read (word ender){ byte dado; I2C_TxStart(); I2C_TxByte (ADDR); I2C_TxByte ((byte) (ender >> 8)); I2C_TxByte ((byte) (ender & 0xFF)); I2C_TxStart(); I2C_TxByte (ADDR | 1); dado = I2C_RxByte (); I2C_TxStop(); return dado;}
Comunicação 3 Fios
Comunicação 3 Fios
• DS1302 (Clock/Calendar + Ram)– Relógio: 12/24 horas, precisão 0.01 seg– Calendário: 100 anos– Alarme: data + horário– 31 bytes de Ram (0x10 a 0cFF)– Baixo consumo p/ uso de bateria– Uso com cristal de 32KHz– Clock comunicação até 2MHz (5V)– Suporte a bateria recarregável
Comunicação 3 Fios
Comunicação 3 Fios
• A4 a A0 é o endereço da RAM ou do registrador do relógio
• Modo burst p/ ler ou escrever tudo
Comunicação 3 Fios
byte DS1302_RxByte (){ int i; byte dado; for (i = 0; i < 8; i++) { delay_us (1); dado = dado >> 1; if (input (DS1302_IO)) dado |= 0x80; output_high (DS1302_SCLK); delay_us (1); output_low (DS1302_SCLK); } return dado;}
PIC void DS1302_TxByte (byte b){ int i; set_tris_a (0xD8); for (i = 0; i < 8; i++) { if (b & 1) output_high (DS1302_IO); else output_low (DS1302_IO); delay_us (1); output_high (DS1302_SCLK); delay_us (1); output_low (DS1302_SCLK); b = b >> 1; } set_tris_a (0xDA);}
Comunicação 3 Fios
byte DS1302_Read (byte ender){ byte result; output_high (DS1302_CE); delay_us (2); DS1302_TxByte (ender|0x81); result = DS1302_RxByte (); delay_us (2); output_low (DS1302_CE); return result;}
void DS1302_Write (byte ender, byte dado){ output_high (DS1302_CE); delay_us (2); DS1302_TxByte (ender | 0x80); DS1302_TxByte (dado); delay_us (2); output_low (DS1302_CE);}
Comunicação 4 Fios - SPI
Comunicação 4 Fios - SPI
• CPOL – Polaridade do Clock• CPHA – Fase do Clock
Modo CPOL CPHA Clock repouso Borda p/ gravar
0 0 0 0 Subida
1 0 1 0 Descida
2 1 0 1 Descida
3 1 1 1 Subida
• Microwire– Subset restrito do SPI– Somente modo 0– Half duplex
Comunicação 4 Fios - SPI
• 93C66 – EEProm 4096bits (256x16 ou 512x8)– Não volátil (40 anos)– 1.000.000 ciclos de apagamento– Microwire 250KHz a 3 MHz– Tempo de escrita: 15ms a 5ms
Comunicação 4 Fios - SPI
Atenção!
Comunicação 4 Fios - SPI
HCS08 SPI V3• Gera clock• Gerencia shift• Gera/Trata –CS• Interrupções• Duas opções de pinos
Comunicação 4 Fios - SPI // IniciaçãoSOPT2_SPIPS = 1; // pinosSPIC1 = 0x52; // hab SPI, Mode 0SPIC2 = 0x00; // MISO/MOSISPIBR = 0x05; // busclk/64// Write EnablePTBD_PTBD7 = 1;for (i = 0; i < 100; i++) ;WriteSPI (0x04); WriteSPI (0xFF);for (i = 0; i < 100; i++) ;PTBD_PTBD7 = 0;// Envia um bytevoid WriteSPI (byte b) { while (!SPIS_SPTEF) ; SPID = b; }
void Grava (byte addr, word dado) { int i; PTBD_PTBD7 = 1; for (i = 0; i < 100; i++) ; WriteSPI (0x05); WriteSPI (addr); WriteSPI (dado >> 8); WriteSPI (dado & 0xFF); for (i = 0; i < 100; i++) ; PTBD_PTBD7 = 0; for (i = 0; i < 100; i++) ; PTBD_PTBD7 = 1; while (PTAD_PTAD0 == 0) ; PTBD_PTBD7 = 0; for (i = 0; i < 100; i++) ;}
Comunicação 4 Fios - SPI
byte ReadSPI () { byte d; int i, j; for (i = 0; i < 8; i++) { PTBD_PTBD6 = 1; for (j = 0; j < 100; j++) ; d = (d << 1) | PTAD_PTAD0; PTBD_PTBD6 = 0; for (j = 0; j < 100; j++) ; } return d;}
word Le (byte addr) { byte d1, d2; int i; PTBD_PTBD7 = 1; for (i = 0; i < 100; i++) ; WriteSPI (0x06); WriteSPI (addr); for (i = 0; i < 100; i++) ; SPIC1 = 0x12; // desab SPI d1 = ReadSPI(); d2 = ReadSPI(); SPIC1 = 0x52; // hab SPI for (i = 0; i < 100; i++) ; PTBD_PTBD7 = 0; return (d1 << 8) | d2;}
LCD Múltiplos Segmentos
• Cristal líquido, polarização muda com tensão• Segmento pode ter forma variada• Segmentos organizados em matriz
LCD Múltiplos Segmentos
• Controlador de LCD do MC9S08LL16– Até 32 segmentos– Segto backplane = comum p/ multiplexação (até 8)– LCDPENx=pinos em uso, BPENx=backplanes– LCDWFx define fases em que BPs são ativados– LCDWFx contem estados dos FPs p/ cada fase
LCD Múltiplos Segmentos
// Todos os pinos em usoLCDPEN0 = 0xFF;LCDPEN1 = 0xFF;LCDPEN2 = 0xFF;LCDPEN3 = 0xFF;// Comum = LCD 3,6,9,1,7,4 e 8LCDBPEN0 = 0xDB;LCDBPEN1 = 0x03;// Fases – BPs separadosLCDWF0 = 0x01;LCDWF3 = 0x02;LCDWF6 = 0x04;LCDWF9 = 0x08;LCDWF1 = 0x10;LCDWF7 = 0x20;LCDWF4 = 0x40;LCDWF8 = 0x80;
// ConfiguraçãoICSC2 = 0x07; // liga OSCOUTLCDRVC = 0x8b;LCDSUPPLY = 0x83;LCDC0 = 0xA7; LCDBCTL_BRATE = 4;// Acende Logotipo Freescale// BP=LCD0 (fase 0x01) FP=LCD16LCDWF16 |= 1;// Apaga Logotipo FreescaleLCDWF16 &= 0xFE;
Fonte:DEMOLL_Documentation- Demoboard_LCD_Glass.pdf- DEMO9S08LL16_SCH.pdf
LCD Múltiplos Segmentos
// Gerador p/ Dígitosconst unsigned char digito[] ={ /*0*/ 0x97,0xE3, /*1*/ 0x12,0x00, /*2*/ 0x8b,0xb1, /*3*/ 0x9b,0x91, /*4*/ 0x1A,0x12, /*5*/ 0x99,0x93, /*6*/ 0x99,0xb3, /*7*/ 0x13,0x01, /*8*/ 0x9b,0xb3, /*9*/ 0x9b,0x93 };
void MostraDigito (int pos, int dig){ dig = dig << 1; switch (pos) { case 0: LCDWF2 = digito[dig]; LCDWF5 = digito[dig+1]; break; // etc... }}
LCD Alfanumérico
• Organizado em linhas e colunas• Típico 2 x 16, 2 x 20• Padrão de facto: HD44780• Memória no display guarda caracteres• Interface paralela com µControlador• Pinagem varia conforme fabricante/modelo
LCD Alfanumérico
• E – clock para os dados– Descida executa a escrita– Subida executa a leitura
• R/S– 0=comando/status – 1=memória
• R/W– 0=escrita – 1=leitura
• DB0-DB7 – dados
LCD Alfanumérico
LCD Alfanumérico
LCD Alfanumérico
• Reduzindo os pinos de E/S necessários– Default = 8 de dados, 3 de controle– Modo 4 vias de dados
• “sambadinha” para iniciar• Pulsar E duas vezes para cada byte
– Trabalhar só com escrita (R/W = 0)– Shift register para reduzir mais
LCD Alfanumérico
• Memória DDRam– Primeira linha 0x00-0x27– Segunda linha 0x40-0x67– Memória total 80 bytes - shift
• Memória CGRam– Caracteres Personalizados (0x00 a 0x07)– Matriz 5x8, 8 bytes, 1=aceso
LCD Alfanumérico
LCD Alfanumérico
LCD Alfanumérico
byte lcd_le_byte(){ byte dado; input (lcd_d4); input (lcd_d5); input (lcd_d6); input (lcd_d7); output_high(lcd_rw); output_high(LCD_ENAB); delay_cycles (2); dado = 0; if (input(lcd_d7)) bit_set(dado, 7); if (input(lcd_d6)) bit_set(dado, 6); if (input(lcd_d5)) bit_set(dado, 5); if (input(lcd_d4)) bit_set(dado, 4);
PIC
delay_cycles (2); output_low(LCD_ENAB); delay_cycles (4); output_high(LCD_ENAB); delay_cycles (2); if (input(lcd_d7)) bit_set(dado, 3); if (input(lcd_d6)) bit_set(dado, 2); if (input(lcd_d5)) bit_set(dado, 1); if (input(lcd_d4)) bit_set(dado, 0); output_low(LCD_ENAB); delay_cycles (2); return dado;}
LCD Alfanumérico
void lcd_envia_nibble (byte dado){ output_bit(lcd_d4,bit_test(dado,0)); output_bit(lcd_d5,bit_test(dado,1)); output_bit(lcd_d6,bit_test(dado,2)); output_bit(lcd_d7,bit_test(dado,3)); delay_cycles(20); output_high(LCD_ENAB); delay_cycles(20); output_low(LCD_ENAB); delay_cycles(20);}
void lcd_envia_byte (boolean ender, byte dado ){ output_low(lcd_rs); delay_cycles(4); //while (bit_test(lcd_le_byte(),7)) // ; delay_ms(2); output_bit(lcd_rs, ender); output_low(lcd_rw); delay_cycles(4); output_low(LCD_ENAB); delay_us(100); lcd_envia_nibble(dado >> 4); lcd_envia_nibble(dado & 0x0f); output_low(lcd_rs); delay_us(100);}
LCD Alfanuméricobyte CONST INI_LCD[] = {0x20 | (lcd_type << 2), 0xf, 1, 6};void lcd_ini(){ byte c; output_low(lcd_d4); output_low(lcd_d5); output_low(lcd_d6); output_low(lcd_d7); output_low(lcd_rs); output_low(lcd_rw); output_low(LCD_ENAB); delay_ms(15); for (c = 0; c < 3; c++) { lcd_envia_nibble(3); delay_ms(10); } lcd_envia_nibble(2); delay_ms(10); for(c = 0; c < sizeof(INI_LCD); c++) lcd_envia_byte(0, INI_LCD[c]);}
// Posicona o cursor na // coluna x da linha y// x = 1 a 16, y = 1 ou 2void lcd_pos_xy(byte x, byte y){ byte e; if (y != 1) e = 0x40; else e = 0; e += x-1; lcd_envia_byte (0, 0x80|e);}
Referências
• Sites dos Fabricantes– http://www.microchip.com– http://www.freescale.com– http://ti.com/msp430
• Wikipedia
Referências
• Livros– PIC Programação em C, Fábio Pereira– HCS08 Unleashed, Fábio Pereira– The Art of Electronics, Horowitz & Hill
Obrigado!
Perguntas?