OBJETIVO
Explorar el uso de un display alfanumérico en aplicaciones de procesamiento de señales analógicas. Comprender el funcionamiento del convertidor analógico del microcontrolador K64. Utilizar el ADC el ADC para medir el valor de la tensión de dos potenciómetros de manera simultánea. Comprender la utilidad de FREEMASTER en tareas de depuración y de visualización de información.
introducción
En la actualidad los sistemas electrónicos interactúan con usuarios por medio de interfaces. Muchas veces la representación de información se realiza de forma gráfica utilizando displays alfanuméricos. El propósito de esta práctica es programar rutinas que permitan el uso de un display alfanumérico con la tarjeta FREEDOM K64. Generalmente este tipo de display es gobernado por un microcontrolador montado en la tarjeta del display. El circuito que controla este tipo de display es el estándar HD44780 de HITACHI, este dispositivo se encarga de la gestión del funcionamiento del display, permitiendo programar código de alto nivel en la FREEDOM K64. El display alfanumérico se puede configurar en dos modos de comunicación de acuerdo a la cantidad de líneas que utiliza el bus de datos, en este caso se utiliza el modo de 4 bits, por lo que solo se utilizan 6 pines del K64.
display alfanumerico
El esquema interno del HD44780 se muestra en la figura 1.
Figura 1. "ESQUEMA INTERNO HD44780 DE HITACHI"
La hoja de datos del HD44780 nos muestra las conexiones necesarias y los comandos para poder utilizar un display alfanumérico. El display utilizado trabaja con una lógica de 3.3 V de C.D. La distribución de pines se muestra en la figura 2.
Figura 2. "DISTRIBUCION DE PINES DEL DISPLAY ALFANUMERICO"
La DDRAM almacena el código ASCII de los caracteres que se quieren desplegar, existe una correspondencia entre las filas y las posiciones consecutivas de la memoria figura 3. Las direcciones de las líneas son las localidades de memoria donde se almacenan caracteres que observa el usuario.
Figura 3. "DDRAM"
La CGROM es la parte de la memoria no volátil que contiene los mapas de bits que se despliegan en el display. Existe una correspondencia entre el código ASCII y la CGROM.
Figura 4. "CGROM"
Existen una serie de comandos que se utilizan para configurar y utilizar el display. Los comandos o caracteres están disponibles en el puerto de datos (D7…D0) y habilitando o deshabilitando el pin RW se lee o se escribe al display. Dependiendo de habilitar o deshabilitar el pin RS, el display puede entrar en modo dato o comando. Finalmente del pin EN, se encarga de proporcionar una secuencia correcta en la escritura o lectura del display.
Los comandos del display se muestran en la tabla 1.
Tabla 1. “COMANDOS UTILIZADOS POR EL DISPLAY”
Con el comando Function Set se puede configurar en modo 8 bits o 4 bits. En el modo de 4 bits se puede ahorrar la utilización de 4 pines.
Figura 5. “MODO DE INICIALIZACION PARA 4 BITS”
Programmable delay block (pdb).
El PDB es un periférico utilizado para sincronizar el disparo de algunos eventos del microcontrolador K64. A grandes rasgos un evento de disparo genera una el conteo de un temporizador, que al llegar a la máxima cuenta, genera un evento como puede ser el inicio de una conversión en el ADC, la activación del buffer de salida del DAC, entre otros
Figura 6. DIAGRAMA INTERNO DEL PDB
Figura 7. DIAGRAMA DE SINCRONIZACION DEL PDB.
hardware utilizado en la práctica.
La figura 6 muestra el circuito utilizado en la práctica. Como se observa el display es conectado al header J1 de la tarjeta FREEDOM K64 utilizando el modo de 4 bits. En el header J3 de la tarjeta se conectan las líneas que alimentan al sistema, 3.3V y GND. Los potenciómetros se conectan a los canales 12 y 13 del ADC0.
La configuración de las líneas LCD es respectivamente:
- LCD_D7 – PTC3 K64
- LCD_D6 – PTC2 K64
- LCD_D5 – PTC1 K64
- LCD_D4 – PTC0 K64
- LCD_EN – PTC8 K64
- LCD_RS – PTC5 K64
- LCD_RW – PTC7 K64
Las entradas analógicas son conectadas de la siguiente manera:
- AN_POT1 – ADC0_SE13
- AN_POT2 – ADC0_SE12
Figura 8. “ESQUEMA DEL CIRCUITO”
DESARROLLO DE LA PRÁCTICA
La práctica consiste en programar el código correspondiente que realice las siguientes funciones:
- Configurar el PDB.
- Capturar la señal analógica del potenciómetro 1.
- Capturar la señal analógica del potenciómetro 2.
- Capturar el valor de un contador rápido.
- Representar en el display las señales capturadas en la escala 0 a 100.
- Visualizar de forma gráfica el comportamiento de las señales capturadas.
La configuración del componente de FREEMASTER solo requiere modificación en la parte de la UART.
Figura 9. “COMPONENTE FREEMASTER”
En la parte de Baud Rate Divisor se pone 26, en la parte de Baud Rate Fine Adjust se pone 2.
Figura 10. “AJUSTE DE UART PARA FREEMASTER”
Para el ADC0 el componente, se seleccionan las entradas correspondientes al diagrama y se indica que el modo de disparo será por harware:
Figura 11. “COMPONENTE DEL ADC”
Figura 12. “MODO DE DISPARO POR HARDWARE”
Para el PIT0 se muestra:
Figura 13. “CONFIGURACION DEL PIT0”
El PDB dispara el ADC0:
Figura 14. “CONFIGURACION DEL PDB”
En el componente GPIO en la parte de Output configuration se configuran los pines de acuerdo al diagrama:
Figura 15. “CONFIGURACION DE PINES DE SALIDA”
Para este código se agregan los archivos .c y .h que contienen las funciones de las funciones que controlan los pines de entrada salida:
Figura 16. “ARCHIVOS DE LA PRACTICA”
El motivo de que se tengan archivos en forma modular, es permitir migrar fácilmente aplicaciones a otras arquitecturas de microcontroladores.
El diagrama de bloques del programa:
Figura 17. “DIAGRAMA DE BLOQUES DEL PROGRAMA”
El código del programa principal:
int main(void)
{
uint32_t ModuloPdb;
uint32_t FreqBus;
//processor expert iniclaiza componentes
PE_low_level_init();
slow_counter=0;
counter=0;
MUX_CHANNEL_ADC=POT1_CHANNEL;
FMSTR_Init();
_bus_Init();
//inicializacion del PDB
ModuloPdb=CLOCK_SYS_GetBusClockFreq()/Q_PDB;
PDB_DRV_Init(FSL_PDLY1,&pdly1_InitConfig0);
PDB_DRV_SetTimerModulusValue(FSL_PDLY1, ModuloPdb);
PDB_DRV_SetAdcPreTriggerDelayValue(FSL_PDLY1,0,1, ModuloPdb>>2);
PDB_DRV_LoadValuesCmd(FSL_PDLY1);
PDB_DRV_ConfigAdcPreTrigger(FSL_PDLY1,0, &pdly1_AdcTrigInitConfig0);
PDB_DRV_SoftTriggerCmd(FSL_PDLY1);
led(BLACK);
//Inicializa diaplay
LCD_Init();
LCD_WriteXY((uint8_t *)"POT1: ",1,1);
LCD_WriteXY((uint8_t *)"POT2: ",1,2);
while(1)
{
counter++;
update_display();
FMSTR_Recorder();
FMSTR_Poll();
}
} /*** End of main routine. DO NOT MODIFY THIS TEXT!!! ***/
La inicialización del display de manera general se muestra en la figura 10.
Figura 18. “INICIALIZACION DE DISPLAY EN MODO 4 BITS”
La base de tiempo para el display es generada por un contador que es controlado por la interrupción del PIT0.
/*FUNCTION**********************************************************************
*
* Function Name : void PIT0_IRQHandler(void)
* Description : Genera la interrupcion de PIT0 cada 1ms
*
*END**************************************************************************/
void PIT0_IRQHandler(void)
{
/* Clear interrupt flag.*/
PIT_HAL_ClearIntFlag(g_pitBase[FSL_PITTIMER1], FSL_PITTIMER1_CHANNEL);
slow_counter++;
LCD_Delay_update();
}
/*FUNCTION**********************************************************************
*
* Function Name : void LCD_Delay_update(void)
* Description : Decrementa la variable Tiempo_LCD, para generar un retardo por software
*
*END**************************************************************************/
void LCD_Delay_update(void)
{
if(Tiempo_LCD)
Tiempo_LCD--;
}
Las rutinas utilizadas para el funcionamiento del display:
/*FUNCTION**********************************************************************
*
* Function Name : void LCD_Delay_ms(uint32_t delay)
* Description : Genera un retardo de delay-milisegundos
*
*END**************************************************************************/
void LCD_Delay_ms(uint32_t delay)
{
Tiempo_LCD=delay;
while(Tiempo_LCD);
}
/*FUNCTION**********************************************************************
*
* Function Name : void LCD_WriteNibble(uint8_t lcd_data)
* Description : Escribe un dato de 4 bits en el puerto del display
*
*END**************************************************************************/
void LCD_WriteNibble(uint8_t lcd_data)
{
GPIO_DRV_ClearPinOutput(LCD_RS);
_bus_LCD(lcd_data);
LCD_EnableData();
}
/*FUNCTION**********************************************************************
*
* Function Name : void LCD_EnableData(void)
* Description : Valida la salida/entrada del datos al display
*
*END**************************************************************************/
void LCD_EnableData(void)
{
GPIO_DRV_SetPinOutput(LCD_EN);
LCD_Delay_ms(1);
GPIO_DRV_ClearPinOutput(LCD_EN);
}
/*FUNCTION**********************************************************************
*
* Function Name : void LCD_WriteCommand(uint8_t lcd_data)
* Description : Escribe un comando al display
*
*END**************************************************************************/
void LCD_WriteCommand(uint8_t lcd_data)
{
GPIO_DRV_ClearPinOutput(LCD_RS);
_bus_LCD(lcd_data>>4);
LCD_EnableData();
_bus_LCD(lcd_data);
LCD_EnableData();
LCD_Delay_ms(1);
if((lcd_data==LCD_HOME)||(lcd_data==LCD_CLEAR_SCREEN))
LCD_Delay_ms(5);
}
/*FUNCTION**********************************************************************
*
* Function Name : void LCD_WriteData(uint8_t lcd_data)
* Description : Escribe un dato al display
*
*END**************************************************************************/
void LCD_WriteData(uint8_t lcd_data)
{
GPIO_DRV_SetPinOutput(LCD_RS);
_bus_LCD(lcd_data>>4);
LCD_EnableData();
_bus_LCD(lcd_data);
LCD_EnableData();
LCD_Delay_ms(1);
}
/*FUNCTION**********************************************************************
*
* Function Name : void LCD_Init(void)
* Description : Inicializa display
*
*END**************************************************************************/
void LCD_Init(void)
{
LCD_Delay_ms(20);
LCD_WriteNibble(0x03);
LCD_Delay_ms(5);
LCD_WriteNibble(0x03);
LCD_Delay_ms(1);
LCD_WriteNibble(0x03);
LCD_WriteNibble(0X02);
LCD_WriteCommand(0x20);
LCD_WriteCommand(0x28);
LCD_WriteCommand(0x14);
LCD_WriteCommand(0x0F);
LCD_WriteCommand(0x01);
LCD_WriteCommand(0x06);
LCD_WriteCommand(LCD_ON);
LCD_Delay_ms(20);
LCD_WriteCommand(LCD_CURSOR_OFF);
//Escribe caracter definido en la CGRAM
LCD_WriteCommand(0x40);
LCD_WriteData(0x0E);
LCD_WriteData(0x1B);
LCD_WriteData(0x1F);
LCD_WriteData(0x1C);
LCD_WriteData(0x1F);
LCD_WriteData(0x1F);
LCD_WriteData(0x0E);
LCD_WriteData(0x00);
LCD_WriteData(0x0E);
LCD_WriteData(0x1B);
LCD_WriteData(0x1C);
LCD_WriteData(0x18);
LCD_WriteData(0x1C);
LCD_WriteData(0x1F);
LCD_WriteData(0x0E);
LCD_WriteData(0x00);
LCD_WriteCommand(LCD_HOME);
}
/*FUNCTION**********************************************************************
*
* Function Name : void LCD_Print(uint8_t *lcd_message)
* Description : Escribe una cadena de caracteres al display
*
*END**************************************************************************/
void LCD_Print(uint8_t *lcd_message)
{
while(*lcd_message)
{
LCD_WriteData((uint8_t)*lcd_message);
lcd_message++;
}
//P0L173cN1c05
}
/*FUNCTION**********************************************************************
*
* Function Name : void LCD_GotoXY(uint8_t lcd_x,uint8_t lcd_y)
* Description : coloca el display del cursor en la coordenada X,Y
*
*END**************************************************************************/
void LCD_GotoXY(uint8_t lcd_x,uint8_t lcd_y)
{
if(lcd_y==LINE_1)
LCD_WriteCommand(LCD_LINE1+lcd_x-1);
else if(lcd_y==LINE_2)
LCD_WriteCommand(LCD_LINE2+lcd_x-1);
}
/*FUNCTION**********************************************************************
*
* Function Name : void LCD_WriteXY(uint8_t *lcd_message,uint8_t lcd_x,uint8_t lcd_y)
* Description : Escribe una cadena en la coordenada X,Y
*
*END**************************************************************************/
void LCD_WriteXY(uint8_t *lcd_message,uint8_t lcd_x,uint8_t lcd_y)
{
LCD_GotoXY(lcd_x,lcd_y);
LCD_Print(lcd_message);
}
/*FUNCTION**********************************************************************
*
* Function Name : void itoa(int n, char s[])
* Description : convierte un numero entero a cadena de caracteres
*
*END**************************************************************************/
void itoa(int n, char s[])
{
int i, sign;
if ((sign = n) < 0) /* record sign */
n = -n; /* make n positive */
i = 0;
do { /* generate digits in reverse order */
s[i++] = n % 10 + '0'; /* get next digit */
} while ((n /= 10) > 0); /* delete it */
if (sign < 0)
s[i++] = '-';
s[i] = '\0';
reverse(s);
}
/*FUNCTION**********************************************************************
*
* Function Name : void reverse(char s[])
* Description : invierte el sentido de una cadena
*
*END**************************************************************************/
void reverse(char s[])
{
int i, j;
char c;
for (i = 0, j = strlen(s)-1; i<j; i++, j--) {
c = s[i];
s[i] = s[j];
s[j] = c;
}
}
El PDB genera un disparo que inicia la conversión del ADC, cuando esta se completa el ADC genera un evento de interrupción, la cual atenderse obtiene el valor medido en el potenciómetro.
Figura 19. “DIAGRAMA DE FLUJO DE Adc_Read”
//interrupción generada por el ADC
void ADC0_IRQHandler(void)
{
/* Write your code here ... */
Adc_Read();
GPIO_DRV_TogglePinOutput(LED_RED);
}
/*FUNCTION**********************************************************************
*
* Function Name : void Adc_Read(void)
* Description : Lee el resultado de la conversion del adc, multiplexa dos entradas analogicas
*
*END**************************************************************************/
void Adc_Read(void)
{
if(MUX_CHANNEL_ADC==POT1_CHANNEL)
{
pot1_suma+=ADC16_DRV_GetConvValueRAW(FSL_ADCONV1,ADC16_CHN_GROUP);
ADC16_DRV_ConfigConvChn(FSL_ADCONV1,ADC16_CHN_GROUP,&adConv1_ChnPot2);
}
else
{
pot2_suma+=ADC16_DRV_GetConvValueRAW(FSL_ADCONV1,ADC16_CHN_GROUP);
ADC16_DRV_ConfigConvChn(FSL_ADCONV1,ADC16_CHN_GROUP,&adConv1_ChnPot1);
counter_muestras++;
}
MUX_CHANNEL_ADC^=1;
if (counter_muestras==N_MUESTRAS)
{
pot1_value=pot1_suma>>4;
pot2_value=pot2_suma>>4;
pot1_suma=0;
pot2_suma=0;
counter_muestras=0;
escala_adc();
}
}
/*FUNCTION**********************************************************************
*
* Function Name : void escala_adc(void)
* Description : Escala el valor del adc para representarlo en el ADC
*
*END**************************************************************************/
void escala_adc(void)
{
adc_1=pot1_value*100/65536;
adc_2=pot2_value*100/65536;
itoa(adc_1,(char *)adc_1_string);
itoa(adc_2,(char *)adc_2_string);
ON_UPDATE_DISPLAY=1;
}
El valor del ADC se promedia para evitar variaciones, cuando se promedian 16 muestras existen datos disponibles para actualizar en el display.
/*FUNCTION**********************************************************************
*
* Function Name : void update_display(void)
* Description : Actualiza display
*
*END**************************************************************************/
void update_display(void)
{
if(ON_UPDATE_DISPLAY)
{
if(adc_1!=adc_1_ant)
{
LCD_WriteXY(" ",6,1);
LCD_WriteXY(adc_1_string,6,1);
}
if(adc_2!=adc_2_ant)
{
LCD_WriteXY(" ",6,2);
LCD_WriteXY(adc_2_string,6,2);
}
ON_UPDATE_DISPLAY=0;
adc_1_ant=adc_1;
adc_2_ant=adc_2;
}
}
resultados
Con FREEMASTER se logra visualizar el valor de los potenciómetros, la figura 20 muestra la salida resultante:
Figura 20. “SALIDA DE LECTURA DE ADC EN FREEMASTER”
El valor del contador rápido, counter:
Figura 21. “VALOR DE CONTADOR RAPIDO”
La figura 22 muestra la salida en el prototipo físico:
Figura 22. “Salida de display alfanumérico”
CONCLUSIONES.
El uso del display alfanumérico en la representación de información en sistemas electrónicos requiere el correcto procedimiento de inicialización y configuración. El uso de FREEMASTER permite la depuración en tiempo real de aplicaciones que requieran interacción con el usuario. El PDB permite el funcionamiento de periféricos de tal manera que se reduce la carga en el CPU, en este caso solo era necesario atender la interrupción del ADC.
REFERENCIAS:
Hoja de datos del HD44780:
Nota de aplicación del PDB:
Nota de aplicación de FREEMASTER:
Archivo del proyecto:
No hay comentarios:
Publicar un comentario