LCD displaying weird characters - Page 1
Forum section: electronics / beginners

author: miketan3904
author_rank: Contributor
timestamp: June 17, 2020, 08:01:28 am
content: Hi! I am using PIC16F887. I am trying to display the ADC result on the LCD but sometimes there was weird character displayed for the voltage displayed on the first line. For the second line, there is no problem at all. Could someone please help me to check if there is any mistake in my code? Thank you!Code:#pragma config CONFIG1 = 0xE0D4#pragma config CONFIG2 = 0xFFFF#include <xc.h>#define _XTAL_FREQ 8000000#define RS PORTCbits.RC0 // LCD RS#define EN PORTCbits.RC1 // LCD RWvoid readVolt();void readAmp();void LCD_Data(unsigned char data);void LCD_Cmd( unsigned char cmd);void LCD_Init();void DisplayString(unsigned char *string);char intToChar (int num);char num;int adcReg;double adcResult1,adcResult,displayADC;void main(){OSCCON = 0X70; // Set internal oscillator to 8MHz;TRISD = 0x00; // Configure PORTD as outputTRISC = 0x00; // Configure PORTB as outputTRISA = 0xFF; // Configure PORTA as inputADCON1 = 0xB0; // ADC result right-justified, VSS & VDD as referenceANSEL = 0xFF; // Configure AN0-AN7 (RA0-RA5 & RE0-RE2) as analog inputLCD_Init(); // Initialize LCDwhile (1){readVolt();__delay_ms(500);}}/* READ PEAK VOLTAGE USING ADC CH0 */void readVolt(){ADCON0 = 0x81 | (0 << 2); // FOSC/32, enable ADC, select ADC channel__delay_ms(1); // Delay for internal capacitor to chargeGO_nDONE = 1; // Start conversionwhile (GO_nDONE); // Wait for conversion to completeadcReg = (ADRESH << 8) + ADRESL; // Combine ADC resultadcResult1 = adcReg*(3.73/(float)1023);adcResult = ((adcReg*(3.73/(float)1023)+0.3)*3*34.2)/0.7071; // Convert digital value to analog valueADON = 0; // Turn off ADCdisplayADC = adcResult * 10000; // 3 d.p. accuracynum = (int)displayADC / 10000;num = (int)displayADC / 1000 % 10;num = (int)displayADC / 100 % 10;num = (int)displayADC / 10 % 10;num = (int)displayADC % 10;LCD_Cmd(0x80); // Go to LCD first lineDisplayString("Voltage:");LCD_Data(intToChar(num));LCD_Data(intToChar(num));LCD_Data(intToChar(num));LCD_Data('.');LCD_Data(intToChar(num));LCD_Data(intToChar(num));LCD_Data('V');displayADC = adcResult1 * 1000;num = (int)displayADC / 1000;num = (int)displayADC / 100 % 10;num = (int)displayADC / 10 % 10;num = (int)displayADC % 10;LCD_Cmd(0xC0);DisplayString("Voltage: "); // Print first lineLCD_Data(intToChar(num));LCD_Data('.');LCD_Data(intToChar(num));LCD_Data(intToChar(num));LCD_Data(intToChar(num));LCD_Data('V');}/* SEND INSTRUCTION TO LCD */void LCD_Cmd( unsigned char cmd){PORTD = cmd; // Load value to PORTDRS = 0; // Select instruction registerEN = 1; // Send enable pulse__delay_ms(1);EN = 0;}/* SEND DATA TO LCD*/void LCD_Data(unsigned char data){PORTD = data; // Load value to PORTDRS = 1; // Select data registerEN = 1; // Send enable pulse__delay_ms(1);EN = 0;}/* LCD INITIALIZATION BY INSTRUCTION */void LCD_Init(){__delay_ms(100);LCD_Cmd(0x30);__delay_ms(5);LCD_Cmd(0x30);__delay_us(150);LCD_Cmd(0x30);__delay_us(150);LCD_Cmd(0X38);__delay_us(60);LCD_Cmd(0x08);__delay_us(60);LCD_Cmd(0x01);__delay_ms(5);LCD_Cmd(0x06);__delay_us(60);LCD_Cmd(0x0C);__delay_us(60);}/* DISPLAY STRING ON LCD */void DisplayString(unsigned char *string){while(*string){LCD_Data(*string++);}}char intToChar (int num){switch (num){case 0: return '0'; break;case 1: return '1'; break;case 2: return '2'; break;case 3: return '3'; break;case 4: return '4'; break;case 5: return '5'; break;case 6: return '6'; break;case 7: return '7'; break;case 8: return '8'; break;case 9: return '9'; break;}}

author: Psi
author_rank: Super Contributor
timestamp: June 17, 2020, 08:23:35 am
content: This seems wrongadcResult = ((adcReg*(3.73/(float)1023)+0.3)*3*34.2)/0.7071;I assume adcReg is 0-1023 raw ADC? When i put that formula into excel 0-1023 goes between 43 and 584 ??If adcResult is ever 10 (or higher) then it gets multiplied by 10000 to become 100000.Then displayADC = adcResult * 10000; becomes...num = 100000 / 10000Which makes num = 10And this is then passed into intToChar() which has no case to handle above 9 and returns an undefined number.Add a return at the end of intToChar. maybe return 'X'Then see if you ever get an X

author: miketan3904
author_rank: Contributor
timestamp: June 17, 2020, 08:35:10 am
content: adcResult represents the rms value of mains voltage, so it will always around 240V. I do not get 'X', but sees sigma, pi, ohm or p on the LCD. How should I correct this? I can't change the calculation because that's the math that I need to do because i have some scaling in my hardware. And for the times 10000, that's to separate the digits.

author: Psi
author_rank: Super Contributor
timestamp: June 17, 2020, 08:39:59 am
content: Quote from: miketan3904 on June 17, 2020, 08:35:10 amSo you added my suggested return 'x' line and didnt get an x?

author: miketan3904
author_rank: Contributor
timestamp: June 17, 2020, 08:53:53 am
content: Yes, followed what you suggested and did not get 'X', sometimes will see some pi symbol instead. And when the LCD is displaying number, the adcResult is wrong. For example the adcResult1 displayed was 3.591V and the adcResult displaye was 51.36. If I do the math, adcResult1 of 3.591 should gives adcResult a value of 282.29.

author: Psi
author_rank: Super Contributor
timestamp: June 17, 2020, 11:35:59 am
content: hm.. If the problem is intermittent then my guess is it's a timing issue talking to the LCD and some bits are being intermittently lost.You maybe switching some LCD pins without a needed delay or with a delay that is too small.

author: Ground_Loop
author_rank: Frequent Contributor
timestamp: June 17, 2020, 06:20:00 pm
content: I had a similar problem with the first character of the first line being intermittently correct. I was driving the display as you are with data direct to the LCD and switching the EN and RS bits. It all came down to making sure the Init routine was solid and correct then adjusting the timing as suggested above to correct the problem. Since then I have moved on to a 16 bit shift register with SPI. Now operates error free with 1MHz clock speed.

author: Renate
author_rank: Super Contributor
timestamp: June 17, 2020, 10:28:25 pm
content: It's strange that the compiler didn't complain about not returning anything in that switch case.Normally, I just do:Code:char digit = x+'0';Or if that doesn't agree with your aesthetics:Code:const char digits="0123456789"; // saying 10 saves the null bytechar digit=digits;You've got a 10 bit ADC, using double variables is a bit overkill.Your ADC LSB is 0.529 on your final reading.Displaying 999.9 is less misleading.I like fixed point math.You could just do it all in integer math:Code:uint16_t adc = whatever;uint32_t x = 346721*adc+29000000;uint16_t fourdigs = (uint16_t) (x>>16);The two constants there are approximate, but close.If you really want five digits, add another zero to the two constants.

author: miketan3904
author_rank: Contributor
timestamp: June 18, 2020, 03:08:32 am
content: Quote from: Renate on June 17, 2020, 10:28:25 pmI apologize for my poor programming knowledge, I don't really understand this part. Could you please explain more on what the code does? Thanks for your help.

author: Renate
author_rank: Super Contributor
timestamp: June 18, 2020, 03:34:15 am
content: Well, we hate floating point because the routines use tons of instructions.We hate integer division because that's slow too.So our equation is: value = (346721*adc+29000000)/65536We don't actually do the division, we just take the two high bytes of the 32 bit result.The value of the ADC LSB = 0.529054 , which is 3.73/1023*3*34.2/0.70710.529054*65536*10 = 346721So 346721*adc+some-constant = 10 * 65536 * what-we-want.Divide that by 65536 and you get 10 * what-we-want.And that is the value we want with one decimal place.

author: miketan3904
author_rank: Contributor
timestamp: June 18, 2020, 06:46:49 am
content: OK, understood. Thanks!