使用 Atmega 驱动液晶显示器时出现问题:第二行没有字符
Problem driving lcd with Atmega: no characters on second line
我有一个项目,其中 8x2 字符液晶显示器由 atmega8 微控制器控制。
液晶显示器是dips082-hnled:https://eu.mouser.com/datasheet/2/127/dips082e-274871.pdf
因为它很容易控制,所以我写了自己的函数来初始化它并向它写入一些文本。
写一些文字真的是我所需要的,为了这个项目...
问题如下:
虽然 lcd 的第一行在我的程序中工作得很好,但我根本无法在第二行显示任何内容。
我查了lcds datasheet和lcd使用的驱动芯片的datasheet,没弄明白是什么问题。
我还检查了第二个已知良好的相同类型的液晶显示器,我遇到了同样的问题,所以它也不会是有故障的液晶显示器。
这是我驱动液晶显示器的所有代码:
#include <xc.h>
#include "pinmapping.h"
#include "main.h"
#include <util/delay.h>
//LCD:
//RS: HIGH=Screen Data, LOW=Command
//RW: HIGH=Read, LOW=Write
//E: FALLING EDGE=Execute Command/Write Data
//D0...D7: PORTD, 8-bit data lines
void toggleEnable(){
//set pin low
PORTB = PORTB & (~LCD_E);
//wait for 3 ms so the lcd has time to execute the instruction
_delay_ms(3);
//set pin high again
PORTB = PORTB | LCD_E;
//allow for a minimal high time
_delay_ms(3);
}
void initLCD(){
//Wait until internal LCD init complete
_delay_ms(25);
//Set RS and RW low
PORTB = PORTB & (~(LCD_RS | LCD_RW));
//Function Set (8-bit data, 2 lines, 5x8 font)
PORTD = 0b00110000;
toggleEnable();
//Display ON/OFF (Display on, Cursor visible, Cursor blink)
PORTD = 0b00001100;
toggleEnable();
//Clear Display, Cursor Home
PORTD = 0b00000001;
toggleEnable();
//Entry Mode set (Cursor auto-increment)
PORTD = 0b00000110;
toggleEnable();
//Set RS and RW high again
PORTB = PORTB | (LCD_RS | LCD_RW);
}
void testLCD(){
char testchars[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvxyz0123456789!?-+,. :;<=>*/()%&#$";
//Set RS low and RW low
PORTB = PORTB & (~LCD_RS);
PORTB = PORTB & (~LCD_RW);
//Clear Display, Cursor Home
PORTD = 0b00000001;
toggleEnable();
//Set RS high and RW low
PORTB = PORTB | LCD_RS;
PORTB = PORTB & (~LCD_RW);
//Write the array contents to the lcd - this should completely fill the DDRAM
for(int i = 0; i < 80; i++){
PORTD = testchars[i];
toggleEnable();
}
}
void writeLCD(char text[16]){
//Set RS low and RW low
PORTB = PORTB & (~LCD_RS);
PORTB = PORTB & (~LCD_RW);
//Clear Display, Cursor Home
PORTD = 0b00000001;
toggleEnable();
//Set RS high and RW low
PORTB = PORTB | LCD_RS;
PORTB = PORTB & (~LCD_RW);
//loop through string send it to lcd
for(int i = 0; i < 16; i++){
//We have a 16 char lcd so we need to jump to line 2 after 8 chars
if(i == 8){
//Set RS low and RW low
PORTB = PORTB & (~LCD_RS);
PORTB = PORTB & (~LCD_RW);
//Set DD-RAM Address to 0x40 - the beginning of line 2
PORTD = 0xC0;
//and execute command
toggleEnable();
//Set RS high and RW low
PORTB = PORTB | LCD_RS;
PORTB = PORTB & (~LCD_RW);
}
//put the current char to data bus
PORTD = text[i];
//then toggle the enable pin to actually write it to the lcd
toggleEnable();
}
//Set RS and RW low
PORTB = PORTB & (~(LCD_RS | LCD_RW));
//reset cursor to start
PORTD = 0b00000010;
toggleEnable();
//Set RS and RW high again
PORTB = PORTB | (LCD_RS | LCD_RW);
}
void commandLCD(char command){
//Set RS and RW low
PORTB = PORTB & (~(LCD_RS | LCD_RW));
PORTD = command;
toggleEnable();
//Set RS and RW high again
PORTB = PORTB | (LCD_RS | LCD_RW);
}
如你所见,我写了一个测试函数,它应该填满整个 80 字节的显示内存,lcd 有。
我正在使用 xc8 编译器。
在 io 端口初始化之后,initLCD 函数在主函数的开头被调用。
稍后从程序的不同部分调用 writeLCD 函数...
pinmapping.h 文件为使用的不同引脚定义了位掩码。 (例如:LCD_RS)
lcd的8条数据线连接到atmega的D端口,匹配它们的位(eg: lcd D0 to avr D0, lcd D1 to avr D1 etc.
也许我忽略了什么或者我做错了什么,但我无法弄清楚是什么导致了问题...
我认为您会在第一行结束后找到第 2 行,它有 40 个字符长,而不管您的显示器每行的实际字符数。
因此,如果您转到第 1 行的第 41 位,您应该开始写入第 2 行。
所以我终于想通了!
我基本上一切都正确,但显然您需要执行两次功能设置命令才能将显示设置为 8 位数据模式和 2 行显示。
显示器的数据表或所用控制器的数据表中都没有提到这一点。
所以这是使它工作的修改后的 initLCD 函数。 (是的,我在测试时翻转了字体位,但根据数据表,这在 2 行显示器上被忽略了)
void initLCD(){
//Wait until internal LCD init complete
_delay_ms(25);
//Set RS and RW low
PORTB = PORTB & (~(LCD_RS | LCD_RW));
//Function Set (8-bit data, 2 lines, 5x8 font)
PORTD = 0b00111100;
toggleEnable();
//Apparently this has to be done twice, to get 2 lines to work
PORTD = 0b00111100;
toggleEnable();
//Display ON/OFF (Display on, Cursor visible, Cursor blink)
PORTD = 0b00001100;
toggleEnable();
//Clear Display, Cursor Home
PORTD = 0b00000001;
toggleEnable();
//Entry Mode set (Cursor auto-increment)
PORTD = 0b00000110;
toggleEnable();
//Set RS and RW high again
PORTB = PORTB | (LCD_RS | LCD_RW);
}
如数据表所示,第 2 行从地址 40 开始。
没有地址 81。写入超过 80 个字符只会覆盖第一个字符。它在 80 后回绕,因此如果您向其中写入 81 个字符,则最后一个字符会覆盖第一个字符。
我有一个项目,其中 8x2 字符液晶显示器由 atmega8 微控制器控制。
液晶显示器是dips082-hnled:https://eu.mouser.com/datasheet/2/127/dips082e-274871.pdf
因为它很容易控制,所以我写了自己的函数来初始化它并向它写入一些文本。 写一些文字真的是我所需要的,为了这个项目...
问题如下:
虽然 lcd 的第一行在我的程序中工作得很好,但我根本无法在第二行显示任何内容。
我查了lcds datasheet和lcd使用的驱动芯片的datasheet,没弄明白是什么问题。
我还检查了第二个已知良好的相同类型的液晶显示器,我遇到了同样的问题,所以它也不会是有故障的液晶显示器。
这是我驱动液晶显示器的所有代码:
#include <xc.h>
#include "pinmapping.h"
#include "main.h"
#include <util/delay.h>
//LCD:
//RS: HIGH=Screen Data, LOW=Command
//RW: HIGH=Read, LOW=Write
//E: FALLING EDGE=Execute Command/Write Data
//D0...D7: PORTD, 8-bit data lines
void toggleEnable(){
//set pin low
PORTB = PORTB & (~LCD_E);
//wait for 3 ms so the lcd has time to execute the instruction
_delay_ms(3);
//set pin high again
PORTB = PORTB | LCD_E;
//allow for a minimal high time
_delay_ms(3);
}
void initLCD(){
//Wait until internal LCD init complete
_delay_ms(25);
//Set RS and RW low
PORTB = PORTB & (~(LCD_RS | LCD_RW));
//Function Set (8-bit data, 2 lines, 5x8 font)
PORTD = 0b00110000;
toggleEnable();
//Display ON/OFF (Display on, Cursor visible, Cursor blink)
PORTD = 0b00001100;
toggleEnable();
//Clear Display, Cursor Home
PORTD = 0b00000001;
toggleEnable();
//Entry Mode set (Cursor auto-increment)
PORTD = 0b00000110;
toggleEnable();
//Set RS and RW high again
PORTB = PORTB | (LCD_RS | LCD_RW);
}
void testLCD(){
char testchars[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvxyz0123456789!?-+,. :;<=>*/()%&#$";
//Set RS low and RW low
PORTB = PORTB & (~LCD_RS);
PORTB = PORTB & (~LCD_RW);
//Clear Display, Cursor Home
PORTD = 0b00000001;
toggleEnable();
//Set RS high and RW low
PORTB = PORTB | LCD_RS;
PORTB = PORTB & (~LCD_RW);
//Write the array contents to the lcd - this should completely fill the DDRAM
for(int i = 0; i < 80; i++){
PORTD = testchars[i];
toggleEnable();
}
}
void writeLCD(char text[16]){
//Set RS low and RW low
PORTB = PORTB & (~LCD_RS);
PORTB = PORTB & (~LCD_RW);
//Clear Display, Cursor Home
PORTD = 0b00000001;
toggleEnable();
//Set RS high and RW low
PORTB = PORTB | LCD_RS;
PORTB = PORTB & (~LCD_RW);
//loop through string send it to lcd
for(int i = 0; i < 16; i++){
//We have a 16 char lcd so we need to jump to line 2 after 8 chars
if(i == 8){
//Set RS low and RW low
PORTB = PORTB & (~LCD_RS);
PORTB = PORTB & (~LCD_RW);
//Set DD-RAM Address to 0x40 - the beginning of line 2
PORTD = 0xC0;
//and execute command
toggleEnable();
//Set RS high and RW low
PORTB = PORTB | LCD_RS;
PORTB = PORTB & (~LCD_RW);
}
//put the current char to data bus
PORTD = text[i];
//then toggle the enable pin to actually write it to the lcd
toggleEnable();
}
//Set RS and RW low
PORTB = PORTB & (~(LCD_RS | LCD_RW));
//reset cursor to start
PORTD = 0b00000010;
toggleEnable();
//Set RS and RW high again
PORTB = PORTB | (LCD_RS | LCD_RW);
}
void commandLCD(char command){
//Set RS and RW low
PORTB = PORTB & (~(LCD_RS | LCD_RW));
PORTD = command;
toggleEnable();
//Set RS and RW high again
PORTB = PORTB | (LCD_RS | LCD_RW);
}
如你所见,我写了一个测试函数,它应该填满整个 80 字节的显示内存,lcd 有。
我正在使用 xc8 编译器。
在 io 端口初始化之后,initLCD 函数在主函数的开头被调用。
稍后从程序的不同部分调用 writeLCD 函数...
pinmapping.h 文件为使用的不同引脚定义了位掩码。 (例如:LCD_RS)
lcd的8条数据线连接到atmega的D端口,匹配它们的位(eg: lcd D0 to avr D0, lcd D1 to avr D1 etc.
也许我忽略了什么或者我做错了什么,但我无法弄清楚是什么导致了问题...
我认为您会在第一行结束后找到第 2 行,它有 40 个字符长,而不管您的显示器每行的实际字符数。
因此,如果您转到第 1 行的第 41 位,您应该开始写入第 2 行。
所以我终于想通了!
我基本上一切都正确,但显然您需要执行两次功能设置命令才能将显示设置为 8 位数据模式和 2 行显示。
显示器的数据表或所用控制器的数据表中都没有提到这一点。
所以这是使它工作的修改后的 initLCD 函数。 (是的,我在测试时翻转了字体位,但根据数据表,这在 2 行显示器上被忽略了)
void initLCD(){
//Wait until internal LCD init complete
_delay_ms(25);
//Set RS and RW low
PORTB = PORTB & (~(LCD_RS | LCD_RW));
//Function Set (8-bit data, 2 lines, 5x8 font)
PORTD = 0b00111100;
toggleEnable();
//Apparently this has to be done twice, to get 2 lines to work
PORTD = 0b00111100;
toggleEnable();
//Display ON/OFF (Display on, Cursor visible, Cursor blink)
PORTD = 0b00001100;
toggleEnable();
//Clear Display, Cursor Home
PORTD = 0b00000001;
toggleEnable();
//Entry Mode set (Cursor auto-increment)
PORTD = 0b00000110;
toggleEnable();
//Set RS and RW high again
PORTB = PORTB | (LCD_RS | LCD_RW);
}
如数据表所示,第 2 行从地址 40 开始。
没有地址 81。写入超过 80 个字符只会覆盖第一个字符。它在 80 后回绕,因此如果您向其中写入 81 个字符,则最后一个字符会覆盖第一个字符。