Pic Lab, PIC16, Experiment #6.2, LCD display

I was working on my personal project – an audio amplifier and needed the LCD display functions, I had already described in my experiments the way to do so, but there were some flaws, so here we are again. What do we need?

  1. Display any info on LCD.
  2. Display the time.
  3. Display the temperature.

Let me tell the story: Initially, I’d bought a display produced by sunlike sc1602, and wasted tons of my time of debugging this piece of silicon. I think I tried all known init procedures I was able to find on the web and still did not see any sign of the life. After that I just bought another LCD from Winstar, and, surprise surprise it initialized immediately by the standard procedure, I was using in my first experiment about LCD.

The display is connected, but the old code has some flaws, that is how I changed lcd.h:

#include <htc.h>
#ifndef _LCD_H_
#define _LCD_H_
#ifndef _XTAL_FREQ
// Unless specified elsewhere, 4MHz system frequency is assumed
#define _XTAL_FREQ 4000000
#define LCD_RS RD2
#define LCD_EN RD3
#define lcd4b RD4
#define lcd5b RD5
#define lcd6b RD6
#define lcd7b RD7
#define    lcd_cursor(x)    lcd_write(((x)&0x7F)|0x80)
#define    LCD_STROBE()    ((LCD_EN = 1),(LCD_EN=0))
#define testbit(data,bitno) ((data>>bitno)&0x01)
extern void lcd_write(unsigned char c);
extern void lcd_clear(void);
extern void lcd_puts(const char * s);
extern void lcd_putch(char c);
extern void lcd_goto(unsigned char pos);
extern void lcd_init();

The main difference in this file – I did not use the whole port for the data as it was done in the previous experiment, only 4 lines were used so only pins are changing their state. So we also need to change two functions: lcd_write and lcd_init.


#include "lcd.h"
lcd_write(unsigned char c)
if (testbit(c,4)) lcd4b = 1; else lcd4b = 0;
if (testbit(c,5)) lcd5b = 1; else lcd5b = 0;
if (testbit(c,6)) lcd6b = 1; else lcd6b = 0;
if (testbit(c,7)) lcd7b = 1; else lcd7b = 0;
if (testbit(c,0)) lcd4b = 1; else lcd4b = 0;
if (testbit(c,1)) lcd5b = 1; else lcd5b = 0;
if (testbit(c,2)) lcd6b = 1; else lcd6b = 0;
if (testbit(c,3)) lcd7b = 1; else lcd7b = 0;
LCD_RS = 0;
lcd_puts(const char * s)
LCD_RS = 1;    // write characters
lcd_putch(char c)
LCD_RS = 1;    // write characters
lcd_write( c );
lcd_goto(unsigned char pos)
LCD_RS = 0;
LCD_RS = 0;
LCD_EN = 0;
__delay_ms(15);    // wait 15mSec after power applied,
lcd4b = 1;
lcd5b = 1;
lcd6b = 0;
lcd7b = 0;
lcd4b = 0;
lcd5b = 1;
lcd6b = 0;
lcd7b = 0;          // Four bit mode
lcd_write(0x28); // Set interface length
lcd_write(0xF); // Display On, Cursor On, Cursor Blink
lcd_clear();    // Clear screen
lcd_write(0x6); // Set entry Mode

Use the next code to check how if it works:

TRISD = 0x00;
lcd_puts("LCD 16x2 test");

That how the success looks like 🙂 :

The time for bullets 2 and 3 has come.

At the moment I wasn’t aware that LCD can be treated like uart interface, by ASCII table, and if you want to send the number you can simply add 0x30 to your number. Instead I made it by predefined arrays here – if you wish to send the data using the minimum possible memory, so far I need just numbers:

const unsigned char digits[10] = {
0b00110000,            //0
0b00110001,            //1
0b00110010,            //2
0b00110011,            //3
0b00110100,            //4
0b00110101,            //5
0b00110110,            //6
0b00110111,            //7
0b00111000,            //8
0b00111001,            //9

Now, we are ready to write functions for periodic refreshing of the display:

void display_tt() {                                                 //дежурная функция отображения информации на LCD
unsigned char d;
get_temp();                                                         //берем значение температуры
LCD_RS = 0;
lcd_write(0b00001100);                                              //выключаем курсор
d = ReadHour();
lcd_putch(digits[d/10]);                                            //выводим значение часов
d = d - ((d/10)*10);
d = 0b00111010;
d = ReadMin();
lcd_putch(digits[d/10]);                                            //выводим значение минут
d = d - ((d/10)*10);
if (!sign) lcd_putch(0b00101011); else lcd_putch(0b10110000);       //знак температуры
lcd_putch(digits[d]);                                               //целое значение температуры
d = 0b00101110;
lcd_putch(d);                                                       //точка
lcd_putch(digits[temp_drob]);                                       //вывод дробного значения
d = 0b11011111;
lcd_putch(d);                                                       //градус
d = 0b01000011;
lcd_putch(d);                                                       //С

in the middle of the debug process…

The next thing is to set up the refreshing rate, I used interrupts and TMR2, since I suspected that I might need other timers for different purposes in a future project. We don’t need to do this quite frequently, so I used all maximum values and even added an extra variable to obtain the refreshing rate of about 32s:

interrupt isr() {
if (TMR2IF) {
if (update == 255) {
update = 0;
TMR2 = 0x01;
T2CKPS0 = 1; T2CKPS1 = 1; //1 делитель не делит входную частоту
TOUTPS2 = 1; TOUTPS1 = 1; TOUTPS3 = 1; TOUTPS0 = 1; //2 делитель делит на 5
TMR2IF = 0;  //сброс флага

Don’t forget that for variables used inside of the interrupt routine the volatile word should be added in the declaration section.

The sources

Leave a Reply

Your email address will not be published.