Vous êtes sur la page 1sur 26

#include <16f876a.

h> // Selecciona el PIC #fuses XT,NOWDT,NOPROTECT,PUT,BROWNOUT // Opciones de configuracin #use delay(clock=4000000) // Velocidad del Cristal : 4 Mhz #use standard_io(B) // PORTB en estandar IO digital #use fixed_io(b_outputs=PIN_B0,PIN_B1,PIN_B2,PIN_B3,PIN_B4,PIN_B5,PIN_B6,PI N_B7) char const RTCCxS=15; // Nmero de RTCC's para 1 segundo con 4 Mhz / 1:256. // VARIABLES GLOBALES int int int int int int int int int nRTCC=0x00; segundo=0x0; minuto=0x0; D1=0x00; D2=0x00; D3=0x00; D4=0x00; l_digit, h_digit; i; // // // // Contador de interrupciones RTCC completas Segundos del Reloj Minutos del Reloj Contenido de los Displays

// Resultado de la conversin bin2bcd // index general // Funcin que testea los displays // Funcin que muestra el contenido del // Funcin que convierte minutos y segundos // Funcin que convierte de Binario a BCD // Interrupcin por desbordamiento // del TIMER0 RTCC

void testdisplays(void); void display_reloj(void); reloj void time2bcd(void); a 4 x BCD void bin2bcd(int valor); #int_RTCC RTCC_isr() { if(++nRTCC==RTCCxS){ nRTCC=0x00; if(++segundo>59){ segundo=0; if(++minuto>59){ minuto=0; } } } } void main(void) { int ksegundo=0x00;

setup_counters(RTCC_INTERNAL,RTCC_DIV_256); // TIMER0: Clock Interno y Preescaler setup_timer_1(T1_DISABLED); setup_timer_2(T2_DISABLED,0,1); setup_comparator(NC_NC_NC_NC); setup_vref(FALSE); enable_interrupts(INT_RTCC);// Habilito Interrupcin RTCC enable_interrupts(global); // Habilito Interrupciones

do{ // Bucle infinito if(segundo!=ksegundo){ time2bcd(); ksegundo=segundo; } display_reloj(); }While(TRUE); } void display_reloj(void){ // Funcin que muestra el contenido del reloj output_b(D1); output_high(PIN_B4); delay_us(30); output_b(D2); output_high(PIN_B5); delay_us(30); output_b(D3); output_high(PIN_B6); delay_us(30); output_b(D4); output_high(PIN_B7); delay_us(30); } void time2bcd(void){ BCD bin2bcd(segundo); actualizo D1 = l_digit; D2 = h_digit; bin2bcd(minuto); actualizo D3 = l_digit; D4 = h_digit; } void bin2bcd(int valor){ // Funcin que convierte de Binario a BCD h_digit=0; if (valor>=10){ do{ valor-=10; h_digit++; } while (valor>=10); } l_digit=valor; } // Funcin convierte minutos y segundos a 4 x // Paso de binario a BCD el segundo y // contenido a mostrar // Paso de binario a BCD el minuto y // contenido a mostrar // Muestro 1er carcter de segundo // Muestro 2do carcter de segundo // Muestro 1er carcter de minuto // Muestro 2do carcter de minuto // si cambia el segundo actualizo las // variables con lo que hay que mostrar

// Muestra constantemente

El Rincn del CCS C -1 Parte

El lenguaje C es el que uso por defecto para el 90% de mis programas. Aqu os muestro algunos ejemplos de cmo hacer cosas con este idioma. Todos ejemplos que se muestran en esta seccin se han realizado usando el PCW PICC de CCS (Veriones 3.242 a 4.096 segn depende) Ms CCS C : 1 2 3 4
ndice General: Template para el uso de la Interrupcin RTCC mediante el TIMER0 La primera y quizs la mas utilizada de las interrupciones, por ejemplo es la bsica para los Relojes en Tiempo (casi) Real. Template para el uso de la Interrupcin RDA de la USART Utilsima para recibir datos desde el PC mientras nuestro micro se dedica a otra cosa mariposa. Una aplicacin prctica de RTCC : Cronopic 1.0 Contando segundos y minutos con nuestro PIC y mostrndolos sobre 4 displays de 7 segmentos multiplexados. BIN2BCD para enviar nuestros bytes a los Displays de 7 segmentos. Flex LCD una completa librera para manejar un mdulo LCD con la posibilidad de configurar los pines a usar. Modificando el ancho de un pulso, generado con RTCC, mediante ordenes RS-232. O un PWM por Software. Una librera para rastrear un Teclado matricial 4x4

Lista para usar (y configurable). Procesador de comandos va RS232 (con Buffer de recepcin) Para dar rdenes con mas enjundia a nuestro PIC Controlando un SERVO con el PIC desde nuestro PC O todo lo que siempre quiso saber sobre los Servomotores y nunca se atrevi a preguntar Strings Demo o como se pueden manejar las cadenas en nuestro PIC No te voy a explicar que sn los punteros o los arrays pero puedes verlos funcionar. RROS v 2.0 La EEPROM interna puesta a nuestro servicio Escribiendo o recuperando datos de la EEPROM interna desde nuestro PC va RS232. Funciones de Date y Time para PIC Coleccin de funciones para manejar Aos, meses, semanas, das, horas, minutos y segundos. Leer y Escribir en la EEPROM interna del PIC variables int 16 bits (2 bytes) Para cuando nuestra variable es mayor que el byte Lib_Int_EEPROM Funciones especiales para Leer y Escribir la EEPROM interna del PIC Para cuando queremos utilizar otras variables distintas al Int8. Rutina de conversin de Nmeros Arbigos a Nmeros romanos Para que nuestro PIC hable en Latn Clsico. Manejando un Array de bits I y Manejando un Array de bits II (simulado en CCS C que no lo implementa) Para que nuestro PIC no desperdicie memoria. Teclado Matricial 4x4 con Driver Analgico Una tcnica para ahorrar pines del pic. Controlando 8 Servos con una sola interrupcin. Una tcnica para conseguir estabilidad y potencia. Teora y Praxis de la Comunicaciones TTL

Hablando con el PIC hasta por los codos.

Serie Tcnicas en C: Tcnicas en C : ndice Un punto de entrada a esta nueva serie de artculos. Tcnicas en C : Midiendo un pulso. 1 Parte Midiendo en tiempo que un pulso permanece en alto mediante INTEXT. Tcnicas en C : Midiendo un pulso. 2 Parte Midiendo en tiempo que un pulso permanece en alto mediante INTCCP en modo Capture. Tcnicas en C : Midiendo un pulso. 3 Parte Midiendo en tiempo que un pulso permanece en alto mediante INTRB. Tcnicas en C : Midiendo un pulso. 4 Parte Midiendo el pulso completo. Periodo y Frecuencia. Tcnicas en C : Generando un pulso. 1 Parte Generando una onda cuadrada simtrica de 2 Khz mediante INTRTCC. Tcnicas en C : Generando un pulso. 2 Parte Generando una onda cuadrada simtrica de 2 Khz mediante INTCCP en modo Compare. Tcnicas en C : Librera de Funciones "tiles" Un artculo muy til sobre funciones tiles y su implementacin en CCS C.

Nota1: Los Micros usados son el 16F628, el 16F876A y el 18F4550 pero puede ser fcilmente adaptado a otros modelos de PIC. Ocasionalmente se usan tambin los 18F1320 y 18F2550. Todos los tiempos estn calculados para cristales de 4 Mhz y 20 Mhz, salvo en los 18F4550 y 18F2550 en funciones de USB que "corren" a 48 Mhz mediante el PLL interno.

Template para el uso de la Interrupcin RTCC mediante el TIMER0 La interrupcin RTCC se produce cada vez que el contador TIMER0 pasa de FFh a 00h. El TIMER0 hace un cmputo completo de 00h a FFh cada 512 S, sin embargo este tiempo puede ser cambiado mediante un preescaler o sea un divisor, ajustable. Los tiempos generados para cada configuracin son (Ver Nota1): :2 :4 :8 :16 :32 :64 :128 :256 -> -> -> -> -> -> -> -> 512 1.0 2.0 4.0 8.1 16.3 33.3 66.6 S al mnimo preescaler posible. mS mS mS mS mS mS mS al mximo preescaler posible.

El Template que propongo usa un Preescaler de 128 para producir una interrupcin RTCC cada 33.3 mS y as cada 30 veces que se produce cambio de estado la variable Flag, o sea 33.3 x 30 = 999 mS. Exactamente este Template es el utilizado en el experimento WinkIntc en el que hacemos parpadear un Led cada 0.25 segundos (aproximadamente).

Template para el uso de la Interrupcin RTCC mediante el TIMER0 #include <16f628.h> // Selecciona el PIC #fuses XT,NOWDT,NOPROTECT,PUT,BROWNOUT // Opciones de configuracin #use delay(clock=4000000) // Velocidad del Cristal : 4 Mhz byte const NInts=30; // Numero de interrupciones para 1 Segundo // VARIABLES GLOBALES char C_Ints=0; Contador de Interrupciones ocurridas char Flag=0; que cambia cada NInts interrupciones #int_RTCC Interrupcin por desbordamiento RTCC_isr() { TIMER0 RTCC // // Flag

// // del

if(C_Ints > NInts){ las ints ocurridas > ints para 1 Seg.

// Si

if(Flag==0){ Flag=1; } else{ Flag=0; } C_Ints=0; // Reinicializo Contador de Ints } ++C_Ints; // Incremento el nmero de interrupciones } // Ocurridas

void main(void) { setup_counters(RTCC_INTERNAL,RTCC_DIV_128);// TIMER0: Clock Interno, Presescaler 128 setup_timer_1(T1_DISABLED); // para una RTCC cada 33.3 milisegundos setup_timer_2(T2_DISABLED,0,1); // -> 1 Segundo = 30 RTCC setup_comparator(NC_NC_NC_NC); setup_vref(FALSE); enable_interrupts(INT_RTCC); // Habilito Interrupcin RTCC enable_interrupts(global); // Habilito Interrupciones

do{ // Bucle infinito if(Flag==K){} else { si ha cambiado Flag ...

//

// AQUI HAGO LO QUE DESEE CADA 1 SEGUNDO k=Flag; Guardo estado anterior de Flag } }While(TRUE); } Descargar Template RTCC Haciendo Click sobre la imagen inferior puede verse un momento de la simulacin realizada con el programa anterior haciendo parpadear un Led conectado a PORTB.0. (La simulacin est realizada con el PIC Simulator IDE 5.22 de Oshon Soft) //

Template para el uso de la Interrupcin RDA de la USART La interrupcin RDA se produce cada vez que en la USART hay disponible un carcter para ser ledo. El buffer de recepcin de la USART del PIC 16F628 dispone de solo 2 bytes por lo que es importantsimo el descargarlo tal como se van recibiendo los caracteres, de esta forma evitamos el que se vean tristemente perdidos en el limbo serie. Este Template demuestra cmo recibir cada carcter que llega hacindole eco desde el programa principal. Si necesitamos recoger muchos caracteres y nuestra rutina principal es larga y farragosa es interesante recibir mediante RDA los caracteres e irlos almacenando en un BUFFER ms amplio para ser posteriormente tratados.

Template para el uso de la Interrupcin RDA de la USART #include <16f628.h> // Selecciona el PIC #fuses XT,NOWDT,NOPROTECT,NOLVP,PUT,BROWNOUT // Opciones de configuracin #use delay(clock=4000000) // Velocidad del Cristal : 4 Mhz #use standard_io(b) #use rs232(baud=9600, xmit=PIN_B2, rcv=PIN_B1) // Definicin del RS232 char Keypress=' '; #int_rda void serial_isr() { Keypress=0x00;

if(kbhit()){ Keypress=getc(); if(Keypress!=0x00){ putchar(keypress); keypress=0x00; } } } void main() { enable_interrupts(global); enable_interrupts(int_rda); printf("\r\n\Listen on RS232 (Int)\r\n"); do { } while (TRUE); } Descargar Template RDA

Una aplicacin prctica de RTCC : Cronopic 1.0 Este artculo nos presenta un mtodo razonable para implementar un cronmetro en nuestro PIC. Hacemos uso en l del Template para el uso de la Interrupcin RTCC mediante el TIMER0 descrito ms arriba as como del Hardware descrito en Hardware de Experimentos : 4 x 7 Segmentos. Para este ejemplo vamos a cambiar de micro y nos mudamos del 16F628 al 16F876A, aunque nuestro programa funcionara exactamente igual en uno que en otro con solo cambiar el include correspondiente. No estara de ms que le dieses un vistazo al artculo Los Cristales y El Tiempo donde discutimos los clculos que despus vamos a utilizar en nuestro Cronopic 1.0. He intentado comentar suficientemente el cdigo fuente, sin embargo debo explicar al menos que tcnica he seguido para desarrollarlo. El asunto es como sigue: Cronopic 1.0 habilita la interrupcin RTCC usando un Cristal de 4 Mhz y con un Preescaler de 1:256 por lo que se produce un desbordamiento cada 66.6 ms. Con 15 interrupciones de stas tenemos 15 * 66.6 = 999 ms, o aproximadamente un segundo. As que lo que vamos a implementar es un contador de segundos que solo se incrementa cada 15 RTCC's

consecutivas. Para esto utilizamos la variable nRTCC que cuando es igual a la constante RTCCxS permite incrementar la variable segundo, que es nuestro contador de segundos transcurridos. Si segundo pasa de 59 incrementamos minuto, y si ste sobrepasa el valor de 59 volvemos a comenzar reinicindolo a 0. Esto dentro de la rutina de tratamiento de la interrupcin RTCC. En el bucle principal, y eterno, dentro de Main() habilitamos una variable ksegundo que si no es igual a segundo nos indica que el segundo actual ha cambiado. Al ocurrir esto disparamos la actualizacin de los valores a sacar por nuestros 7 segmentos. Para ello llamamos a time2bcd() que es la rutina que va a formatear segundo y minuto para que puedan ver visualizados. Inmediatamente hacemos ksegundo igual a segundo para que no volvamos a hacer esto mismo hasta que no cambie el segundo actual, que volveremos a detectar comparndolo con ksegundo. El formateo realizado en time2bcd() consiste en convertir segundo y minuto de sus actuales valores binarios a BCD que es el que acepta el driver de los displays. Esta conversin carga con sus nuevos valores las variables D1 y D2 que son los dgitos Low y Hight en que se convierte segundo y D3 y D4 que son los de minuto. Dentro del bucle principal de main() se llama constantemente a la rutina display_reloj() que es la encargada de poner los valores de D1, D2, D3 y D4 en el driver de los displays. Y eso esto, o casi todo ya que este Cronopic tiene un error de 1 milisegundo por cada segundo contado por lo que no debes tener una fe absoluta en l si tu vida depende de ello. No he querido complicarlo en esta primera versin pero no es difcil compensar este desfase usando el mtodo que he bautizado como pic-bisisesto y que lo realizaremos para versiones posteriores de Cronopic.

Cronopic 1.0 #include <16f876a.h> // Selecciona el PIC #fuses XT,NOWDT,NOPROTECT,PUT,BROWNOUT // Opciones de configuracin #use delay(clock=4000000) // Velocidad del Cristal : 4 Mhz #use standard_io(B) // PORTB en estandar IO digital #use fixed_io(b_outputs=PIN_B0,PIN_B1,PIN_B2,PIN_B3,PIN_B4,PIN_B5,PIN_B6,P IN_B7) char const RTCCxS=15; // Nmero de RTCC's para 1 segundo con 4 Mhz / 1:256. // VARIABLES GLOBALES int nRTCC=0x00; completas int segundo=0x0; int minuto=0x0; // Contador de interrupciones RTCC // Segundos del Reloj // Minutos del Reloj

int int int int int int

D1=0x00; D2=0x00; D3=0x00; D4=0x00; l_digit, h_digit; i;

// Contenido de los Displays

// Resultado de la conversin bin2bcd // index general // Funcin que testea los displays // Funcin que muestra el contenido del // Funcin que convierte minutos y segundos // Funcin que convierte de Binario a BCD // Interrupcin por desbordamiento // del TIMER0 RTCC

void testdisplays(void); void display_reloj(void); reloj void time2bcd(void); a 4 x BCD void bin2bcd(int valor); #int_RTCC RTCC_isr() { if(++nRTCC==RTCCxS){ nRTCC=0x00; if(++segundo>59){ segundo=0; if(++minuto>59){ minuto=0; } } } } void main(void) { int ksegundo=0x00;

setup_counters(RTCC_INTERNAL,RTCC_DIV_256); // TIMER0: Clock Interno y Preescaler setup_timer_1(T1_DISABLED); setup_timer_2(T2_DISABLED,0,1); setup_comparator(NC_NC_NC_NC); setup_vref(FALSE); enable_interrupts(INT_RTCC);// Habilito Interrupcin RTCC enable_interrupts(global); // Habilito Interrupciones do{ // Bucle infinito if(segundo!=ksegundo){ time2bcd(); ksegundo=segundo; } display_reloj(); }While(TRUE); } void display_reloj(void){ // Funcin que muestra el contenido del reloj output_b(D1); output_high(PIN_B4); delay_us(30); output_b(D2); output_high(PIN_B5); delay_us(30); output_b(D3); output_high(PIN_B6); delay_us(30); output_b(D4); // Muestro 1er carcter de segundo // Muestro 2do carcter de segundo // Muestro 1er carcter de minuto // Muestro 2do carcter de minuto // si cambia el segundo actualizo las // variables con lo que hay que mostrar

// Muestra constantemente

output_high(PIN_B7); delay_us(30); } void time2bcd(void){ x BCD bin2bcd(segundo); actualizo D1 = l_digit; D2 = h_digit; bin2bcd(minuto); actualizo D3 = l_digit; D4 = h_digit; } void bin2bcd(int valor){ // Funcin que convierte de Binario a BCD h_digit=0; if (valor>=10){ do{ valor-=10; h_digit++; } while (valor>=10); } l_digit=valor; } // Funcin convierte minutos y segundos a 4 // Paso de binario a BCD el segundo y // contenido a mostrar // Paso de binario a BCD el minuto y // contenido a mostrar

Bin2BCD Cuando queremos utilizar uno de esos drivers para Displays de 7 segmentos que solo aceptan datos en BCD se impone una rutina que nos convierta nuestro byte en tan estrambtico Binary Code Decimal. Ah os dejo una funcin para realizar la necesaria conversin: Bin2BCD (1): int l_digit, h_digit; la conversion bin2bcd // resultado de

void bin2bcd(int valor){ // Funcion que convierte de Binario a BCD h_digit=0;

if (valor>=10){ do{ valor-=10; h_digit++; }while (valor>=10); } l_digit=valor; }

Una librera para manejar un LCD con los Pines que deseemos: flex_lcd.c Aqui os brindo una librera para manejar un LCD con 4 bits de datos, pudiendo establecer los pines que deseemos para ellos y para los de control E, R/W y RS. Slo hay que modificar los #defines de los mismos. Para usarla solo debis incluir el correpondiente #include "flex_lcd.c" en vuestro programa.

flex_lcd.c // flex_lcd.c // Change these pins to fit your own board. #define #define #define #define LCD_DB4 LCD_DB5 LCD_DB6 LCD_DB7 PIN_B4 PIN_B5 PIN_B6 PIN_B7

#define LCD_RS PIN_C0 #define LCD_RW PIN_C1 #define LCD_E PIN_C2 // If you only want a 6-pin interface to your LCD, then // connect the R/W pin on the LCD to ground, and comment // out the following line. #define USE_LCD_RW 1 //======================================== #define lcd_type 2 // 0=5x7, 1=5x10, 2=2 lines #define lcd_line_two 0x40 // LCD RAM address for the 2nd line int8 const LCD_INIT_STRING[4] = { 0x20 | (lcd_type << 2), // Func set: 4-bit, 2 lines, 5x8 dots 0xc, // Display on

1, // Clear display 6 // Increment cursor }; //------------------------------------void lcd_send_nibble(int8 nibble) { // Note: !! converts an integer expression // to a boolean (1 or 0). output_bit(LCD_DB4, !!(nibble & 1)); output_bit(LCD_DB5, !!(nibble & 2)); output_bit(LCD_DB6, !!(nibble & 4)); output_bit(LCD_DB7, !!(nibble & 8)); delay_cycles(1); output_high(LCD_E); delay_us(2); output_low(LCD_E); } //----------------------------------// This sub-routine is only called by lcd_read_byte(). // It's not a stand-alone routine. For example, the // R/W signal is set high by lcd_read_byte() before // this routine is called. #ifdef USE_LCD_RW int8 lcd_read_nibble(void) { int8 retval; // Create bit variables so that we can easily set // individual bits in the retval variable. #bit retval_0 = retval.0 #bit retval_1 = retval.1 #bit retval_2 = retval.2 #bit retval_3 = retval.3 retval = 0; output_high(LCD_E); delay_cycles(1); retval_0 retval_1 retval_2 retval_3 = = = = input(LCD_DB4); input(LCD_DB5); input(LCD_DB6); input(LCD_DB7);

output_low(LCD_E); return(retval); } #endif //--------------------------------------// Read a byte from the LCD and return it. #ifdef USE_LCD_RW int8 lcd_read_byte(void) { int8 low; int8 high; output_high(LCD_RW); delay_cycles(1);

high = lcd_read_nibble(); low = lcd_read_nibble(); return( (high<<4) | low); } #endif //---------------------------------------// Send a byte to the LCD. void lcd_send_byte(int8 address, int8 n) { output_low(LCD_RS); #ifdef USE_LCD_RW while(bit_test(lcd_read_byte(),7)) ; #else delay_us(60); #endif if(address) output_high(LCD_RS); else output_low(LCD_RS); delay_cycles(1); #ifdef USE_LCD_RW output_low(LCD_RW); delay_cycles(1); #endif output_low(LCD_E); lcd_send_nibble(n >> 4); lcd_send_nibble(n & 0xf); } //---------------------------void lcd_init(void) { int8 i; output_low(LCD_RS); #ifdef USE_LCD_RW output_low(LCD_RW); #endif output_low(LCD_E); delay_ms(15); for(i=0 ;i < 3; i++) { lcd_send_nibble(0x03); delay_ms(5); } lcd_send_nibble(0x02); for(i=0; i < sizeof(LCD_INIT_STRING); i++) { lcd_send_byte(0, LCD_INIT_STRING[i]); // // // // // If the R/W signal is not used, then the busy bit can't be polled. One of the init commands takes longer than the hard-coded delay of 60 us, so in that case, lets just do a 5 ms delay

// after all four of them. #ifndef USE_LCD_RW delay_ms(5); #endif } } //---------------------------void lcd_gotoxy(int8 x, int8 y) { int8 address; if(y != 1) address = lcd_line_two; else address=0; address += x-1; lcd_send_byte(0, 0x80 | address); } //----------------------------void lcd_putc(char c) { switch(c) { case '\f': lcd_send_byte(0,1); delay_ms(2); break; case '\n': lcd_gotoxy(1,2); break; case '\b': lcd_send_byte(0,0x10); break; default: lcd_send_byte(1,c); break; } } //-----------------------------#ifdef USE_LCD_RW char lcd_getc(int8 x, int8 y) { char value; lcd_gotoxy(x,y); // Wait until busy flag is low. while(bit_test(lcd_read_byte(),7)); output_high(LCD_RS); value = lcd_read_byte(); output_low(lcd_RS); return(value); } #endif void lcd_setcursor_vb(short visible, short blink) { lcd_send_byte(0, 0xC|(visible<<1)|blink); }

Descargar flex_lcd.c

Modificando el ancho de un pulso, generado con RTCC, mediante ordenes RS232: Este ejemplo nos muestra cmo podemos realizar el ultra famoso led parpadeando, pero realizado con la interrupcin RTCC del Timer0, y recibiendo ordenes va RS232 para poder modificar el ancho del pulso, o la separacin entre dos pulsos sucesivos.

Conceptos, ideas y tcnicas utilizadas: - Cada vez que salta la interrupcin RTCC cambiamos de LOW a HIGH, de HIGH a LOW, el pin RB0. Para ello utilizamos la variable Led que nos indica cual fue el ltimo cambio para poder hacer el complementario. - Cada vez que realizamos dicho cambio de LOW a HIGH, o de HIGH a LOW, cargamos el contador del Timer0 con un valor variable que podemos ajustar externamente: OffsetL para el semiperiodo LOW y OffsetH para el semiperiodo HIGH (Si OffsetL y OffsetH son igual a cero, como es el caso cuando acabamos de resetear el micro, tenemos una onda cuyo periodo es exactamente igual a 2 * RTCC: Una RTCC completa conde ponemos RB0 en HIGH y otra RTCC completa en la que

ponemos RB0 en LOW) - Recibimos distintas ordenes desde el RS232 con las cuales modificamos los valores de OffsetH y OffsetL. - Cualquier valor mayor que 0 en OffsetH o en OffsetL hace que al producirse la correspondiente interrupcin RTCC, que como sabis salta al pasar el Timer0 de FFh a 00h, se carge el Timer0 con dicho valor: Por lo que la siguiente interrupcin ser mas corta, y de forma directamente proporcional al valor cargado, ya que en vez de tener que esperar a que Timer0 recorra todo su contador de 00h a FFh lo va ha hacer solamente desde OffsetH, u OffsetL, hasta FFh. * Cuanto mayor sea OffsetH menos tiempo va a estar RB0 en HIGH. * Cuanto mayor sea OffsetL menos tiempo va a estar RB0 en LOW - Si OffsetH y OffsetL tienen el mismo valor la onda producida tendr iguales ambos semiperiodos, con valores distintos ser por ende distintos ambos y por ello nos variar la frecuencia. Para mantener la frecuencia constante, o el periodo constante, debemos ampliar o disminuir complementariamente ambos Offset. Lo que se incremente uno de ellos debemos disminuir el otro, o viceversa. - Hemos incluido una variable Onda con la que podemos ordenarle a qu parte de nuestro pulso va a afectar la siguiente orden: "C" (de cuadrada para ambos semiperiodos) "H" para el semiperiodo Alto y "L" para el semiperiodo Bajo. Este ejemplito es bsicamente un PWM realizado por Software que puede ser fcilmente adaptado a cualquier micro PIC, por ejemplo para manejar un Servo. Fuente pwm_rtcc_232.c

#include <16f876a.h> #fuses XT,NOWDT,NOPROTECT,NOLVP,PUT,BROWNOUT #use delay(clock=4000000) #use standard_io(b) #use rs232(baud=9600, xmit=PIN_C6, rcv=PIN_C7) int Led=0x00; int Offseth=0x00; int Offsetl=0x00; char Onda='C'; char Keypress=' '; void eco_offset(void); #int_rda void rda_isr() { Keypress=0x00; if(kbhit()){ Keypress=getc();

} } #int_RTCC RTCC_isr(){ // RB0 Switch(Led){ Case 0: output_high(PIN_B0); set_timer0(Offseth); break; Case 1: output_low(PIN_B0); set_timer0(Offsetl); break; } if(++Led>1){ Led=0; } } void main() { setup_counters(RTCC_INTERNAL,RTCC_DIV_2); // TEST enable_interrupts(int_rda); enable_interrupts(global); printf("\r\n\PWM (RTCC) OS\r\n"); Onda='C'; Offseth=0x00; Offsetl=0x00; enable_interrupts(INT_RTCC); do { if(Keypress!=0x00){ switch(Keypress){ // Tipo de Onda case 'c': Onda='C'; break; case 'h': Onda='H'; break; case 'l': Onda='L'; break; // Incrementando y decrementando periodos case '+': if(Onda=='C'){ ++Offseth; ++Offsetl; } if(Onda=='H'){ ++Offseth; } if(Onda=='L'){ ++Offsetl; } break; case '-': if(Onda=='C'){ --Offseth; -Offsetl; } if(Onda=='H'){ --Offseth; } if(Onda=='L'){ --Offsetl; } break; // Periodos Prefijados case '1': if(Onda=='C'){ Offseth=0; Offsetl=0; } if(Onda=='H'){ Offseth=0; } if(Onda=='L'){ Offsetl=0; } break; case '2': if(Onda=='C'){ Offseth=128; Offsetl=128; }

if(Onda=='H'){ Offseth=128; } if(Onda=='L'){ Offsetl=128; } break; case '4': if(Onda=='C'){ Offseth=192; Offsetl=192; } if(Onda=='H'){ Offseth=192; } if(Onda=='L'){ Offsetl=192; } break; } eco_offset(); Keypress=0x00; } } while (TRUE); } void eco_offset(void){ printf("\r\nOnda %c h%u l%u\r\n",Onda,Offseth,Offsetl); }

Descargar pwm_rtcc_232.c

Una librera para rastrear un Teclado matricial 4x4 Aqu tenis una librera que permite leer un teclado matricial de 16 teclas (4 filas x 4 columnas). Solo tenis que incluir con #include esta librera en vuestro programa principal y llamar a la funcin kbd_getc() donde lo necesitis. Nota: Esta librera esta derivada de la original incluida en \drivers del CCS PIC C. En cursiva marco los cambios que he realizado para adaptarla a mi hardware particular. kbd_lib.c ///////////////////////////////////////////////////////////////////// ////// //// kbd_lib.c by Redraven //// //// //// //// Derived from kbdd.c //// //// Generic keypad scan driver //// //// //// //// kbd_init() Must be called before any other function. //// //// //// //// c = kbd_getc(c) Will return a key value if pressed or /0 if not //// //// This function should be called frequently so as //// //// not to miss a key press. ////

//// //// ///////////////////////////////////////////////////////////////////// ////// //// (C) Copyright 1996,1997 Custom Computer Services //// //// This source code may only be used by licensed users of the CCS C //// //// compiler. This source code may only be distributed to other //// //// licensed users of the CCS C compiler. No other use, reproduction //// //// or distribution is permitted without written permission. //// //// Derivative programs created using this software in object code //// //// form are not restricted in any way. //// ///////////////////////////////////////////////////////////////////// /////// ////////////////// The following defines the keypad layout on port D // Un-comment the following define to use port B #define use_portb_kbd TRUE // Make sure the port used has pull-up resistors (or the LCD) on // the column pins #if defined(__PCH__) #if defined use_portb_kbd #byte kbd = 0xF81 // This puts the entire #else #byte kbd = 0xF83 // This puts the entire #endif #else #if defined use_portb_kbd #byte kbd = 6 // on to port B (at address #else #byte kbd = 8 // on to port D (at address #endif #endif #if defined use_portb_kbd #define set_tris_kbd(x) set_tris_b(x) #else #define set_tris_kbd(x) set_tris_d(x) #endif

structure structure

6) 8)

//Keypad connection: (for example column 0 is B0) #define #define #define #define #define #define #define #define COL0 COL1 COL2 COL3 ROW0 ROW1 ROW2 ROW3 (1 (1 (1 (1 (1 (1 (1 (1 << << << << << << << << 0) 1) 2) 3) 4) 5) 6) 7) // // // // // // // // PIN_B0 PIN_B1 PIN_B2 PIN_B3 PIN_B4 PIN_B5 PIN_B6 PIN_B7

#define ALL_ROWS (ROW0|ROW1|ROW2|ROW3) #define ALL_PINS (ALL_ROWS|COL0|COL1|COL2|COL3) // Keypad layout:

char const KEYS[4][4] = {{'1','2','3','A'}, {'4','5','6','B'}, {'7','8','9','C'}, {'*','0','#','D'}};


#define KBD_DEBOUNCE_FACTOR 33 // Set this number to apx n/333 where // n is the number of times you expect // to call kbd_getc each second

void kbd_init() { } char kbd_getc( ) { static byte kbd_call_count; static short int kbd_down; static char last_key; static byte col; byte kchar; byte row; kchar='\0'; if(++kbd_call_count>KBD_DEBOUNCE_FACTOR) { switch (col) { case 0 : set_tris_kbd(ALL_PINS&~COL0); kbd=~COL0&ALL_PINS; break; case 1 : set_tris_kbd(ALL_PINS&~COL1); kbd=~COL1&ALL_PINS; break; case 2 : set_tris_kbd(ALL_PINS&~COL2); kbd=~COL2&ALL_PINS; break;

case 3 : set_tris_kbd(ALL_PINS&~COL3); kbd=~COL3&ALL_PINS; break;

} if(kbd_down) { if((kbd & (ALL_ROWS))==(ALL_ROWS)) { kbd_down=false; kchar=last_key; last_key='\0'; } } else { if((kbd & (ALL_ROWS))!=(ALL_ROWS)) { if((kbd & ROW0)==0) row=0; else if((kbd & ROW1)==0) row=1; else if((kbd & ROW2)==0) row=2; else if((kbd & ROW3)==0) row=3; last_key =KEYS[row][col]; kbd_down = true; } else { ++col;

if(col==4)
col=0;

} } kbd_call_count=0; } set_tris_kbd(ALL_PINS); return(kchar); } Descargar kbd_lib.c

Procesador de comandos va RS232 (con Buffer de

recepcin) Hasta ahora, en nuestros trabajos anteriores, utilizbamos comandos enviados va RS232 consistentes en un nico carcter, con el que hacamos ejecutar alguna de las funciones que nuestro programa implementaba. Este mtodo es de muy corto alcance por dos motivos fundamentales: Primero porque eran ejecutados inmediatamente tan pronto eran recibidos y segundo porque no podamos enviarle datos (una cadena de caracteres) para ser procesados. En este nuevo artculo vamos a solucionar exactamente eso. He imaginado un programa que admite dos comandos de alto nivel: uno de lectura "\r" sin argumentos y otro de escritura "\w" que va seguido de un argumento tan largo como necesitemos. Para manejar el envo de ambos comandos as como el/los argumentos necesarios he implementado lo bsico para manejar el buffer en el PIC, a base de teclas nicas, cada una con su funcin: [INTRO] 0x0D para indicar que hemos terminado de escribir el comando, [RETROCESO] 0x08 para borrar el ltimo carcter enviado y [ESCAPE] 0x1B para borrar todo el contenido actual del buffer. (Como podis imaginar este mini-programa puede complicarse hasta el infinito y solo tenis que aadir comandos a vuestra entera discrecin). El programa va entonces recibiendo caracteres, uno tras otro, y guardndolos en el Buffer. Si recibe el comando [INTRO] habilita un flag para que se procese el comando, y tras ser procesado borra el buffer y vuelve a empezar. Las otras dos teclas de [RETROCESO] y [ESCAPE] las usamos para editar el contenido del buffer antes de enviarle la orden de procesado. He intentado comentar profusamente el programa para que sea meridianamente claro su funcionamiento y hacer as innecesarias mas explicaciones:

command_232_buffered.c #include <16f876a.h> // Definiciones del PIC 16F876A #fuses XT,NOWDT,NOPROTECT,NOLVP,PUT,BROWNOUT // Los Fuses de siempre #use delay(clock=4000000) // Oscilador a 4 Mhz #use rs232(baud=9600, xmit=PIN_C6, rcv=PIN_C7)// RS232 Estndar // CONSTANTES ///////////////////////////////////////////////////////////////// int const lenbuff=32; // Longitud de buffer, Ajustar // a lo que desees (o te sea posible)

// VARIABLES EN RAM /////////////////////////////////////////////////////////// int xbuff=0x00; char cbuff[lenbuff]; char rcvchar=0x00; int1 flagcommand=0; // // // // ndice: siguiente char en cbuff Buffer ltimo carcter recibido Flag para indicar comando disponible

// Declaracin de Funciones /////////////////////////////////////////////////// void inicbuff(void); int addcbuff(char c); void echos(char c); void procesa_comando(void); // // // // Borra buffer aade carcter recibido al buffer Eco selectivo sobre RS232 Procesa comando

// INTERRUPCIONES ///////////////////////////////////////////////////////////// #int_rda void serial_isr() { rcvchar=0x00; if(kbhit()){ rcvchar=getc(); addcbuff(rcvchar); echos(rcvchar); } } // Desarrollo de Funciones //////////////////////////////////////////////////// void echos(char c){ // Echo selectivo ---------------------switch(c){ case 0x0D: printf(" [Ent] "); // Si he pulsado la tecla [Intro] break; case 0x08: printf(" [Del] "); // Si he pulsado la tecla [Retroceso] break; case 0x1B: printf(" [Esc] "); // Si he pulsado la tecla [Escape] break; default: putc(rcvchar); // Echo de cualquier otro carcter } } void inicbuff(void){ // Inicia a \0 cbuff ------------------int i; for(i=0;i<lenbuff;i++){ cbuff[i]=0x00; } xbuff=0x00; } int addcbuff(char c){ // Aade a cbuff ----------------------switch(c){ case 0x0D: // Enter -> Habilita Flag para procesar flagcommand=1; // Comando en Main break; case 0x08: // Del -> Borra ltimo carcter del Buffer if(xbuff>0) cbuff[--xbuff]=0x00; break; // Bucle que pone a 0 todos los // caracteres en el buffer // Inicializo el ndice de siguiente // carcter // Interrupcin recepcin serie USART // // // // // Inicializo carcter recibido Si hay algo pendiente de recibir ... lo descargo y ... lo aado al buffer y ... hago eco (si procede).

case 0x01B: // Esc -> Borra el Buffer completamente inicbuff(); break; default: cbuff[xbuff++]=c; // Aade carcter recibido al Buffer } } // Programa Principal ///////////////////////////////////////////////////////// void main() { inicbuff(); // Borra buffer al inicio printf("\r\n\** RS232 Buffered **\r\n\r\n"); // Presenta men printf("[Enter] Procesa comando\r\n"); printf("[Escape] Borra todo el buffer\r\n"); printf("[Delete] Borra ltimo carcter del buffer\r\n"); printf("[\\w] Comando Escribe\r\n"); printf("[\\r] Comando Lee\r\n"); printf("\r\n"); enable_interrupts(int_rda); // Habilita Interrupcin RDA enable_interrupts(global); // Habilita interrupciones do { if(flagcommand) procesa_comando(); // Si hay comando pendiente // de procesar ... lo procesa. } while (TRUE); } // Procesador de Comandos ///////////////////////////////////////////////////// void procesa_comando(void){ int i; char arg[lenbuff]; // Argumento de comando (si lo tiene) flagcommand=0; // Desactivo flag de comando pendiente. printf("\r\nProcesando ... "); // Monitorizo procesando ... for(i=0;i<lenbuff;i++){ // Bucle que pone a 0 todos los arg[i]=0x00; // caracteres en el argumento } if(cbuff[0]=='\\'&&cbuff[1]=='r'){ // Comparo inicio del buffer con comando "\r" printf("Leyendo ... "); hacer con comando "\r" } if(cbuff[0]=='\\'&&cbuff[1]=='w'){ // Comparo inicio del buffer con comando "\w" i=2; do{ // Extraemos argumento del buffer arg[i-2]=cbuff[i]; // a partir del 3er byte y hasta \0. }while(cbuff[++i]!=0x00); printf("Escribiendo %s ... ",arg);// Aqui lo que deseemos hacer con comando "\w" // Aqui lo que deseemos

// Monitorizamos el argumento. } inicbuff(); // Borro buffer. printf("Procesado.\r\n\r\n"); // Monitorizo procesado.

Vous aimerez peut-être aussi