I2C LCD1602 与 PIC16F877A 接口

问题描述 投票:0回答:1

我和我的伙伴目前正在为 PIC16F877A 微控制器和 LCD1602 I2C 屏幕构建接口代码集

我们可以完美地加载所有内容,但屏幕上没有显示任何内容。我们仔细检查了 PIN 映射并连接了屏幕 I2C 模块上的正确端口。代码编译没有问题并运行到微控制器中,但没有显示。

/* File: config.h */
// [ PIC16F877A ] Configuration Bit Settings
// #pragma config statements should precede project file includes.
// Use project enums instead of #define for ON and OFF.

// CONFIG
#pragma config FOSC = HS
#pragma config WDTE = OFF
#pragma config PWRTE = OFF
#pragma config BOREN = OFF
#pragma config LVP = ON
#pragma config CPD = OFF
#pragma config WRT = OFF
#pragma config CP = OFF

#include <xc.h>



/* File: I2C_LCD.h */
#define _XTAL_FREQ            16000000

#define I2C_BaudRate          100000
#define SCL_D                 TRISC3
#define SDA_D                 TRISC4

#define LCD_BACKLIGHT         0x08
#define LCD_NOBACKLIGHT       0x00
#define LCD_FIRST_ROW         0x80
#define LCD_SECOND_ROW        0xC0
#define LCD_THIRD_ROW         0x94
#define LCD_FOURTH_ROW        0xD4
#define LCD_CLEAR             0x01
#define LCD_RETURN_HOME       0x02
#define LCD_ENTRY_MODE_SET    0x06
#define LCD_CURSOR_OFF        0x0C
#define LCD_UNDERLINE_ON      0x0E
#define LCD_BLINK_CURSOR_ON   0x0F
#define LCD_MOVE_CURSOR_LEFT  0x10
#define LCD_MOVE_CURSOR_RIGHT 0x14
#define LCD_TURN_ON           0x0C
#define LCD_TURN_OFF          0x08
#define LCD_SHIFT_LEFT        0x18
#define LCD_SHIFT_RIGHT       0x1C
#define LCD_TYPE              2 // 0 -> 5x7 | 1 -> 5x10 | 2 -> 2 lines

//-----------[ Functions' Prototypes ]--------------

//---[ I2C Routines ]---

void I2C_Master_Init();
void I2C_Master_Wait();
void I2C_Master_Start();
void I2C_Master_RepeatedStart();
void I2C_Master_Stop();
void I2C_ACK();
void I2C_NACK();
unsigned char I2C_Master_Write(unsigned char data);
unsigned char I2C_Read_Byte(void);

//---[ LCD Routines ]---

void LCD_Init(unsigned char I2C_Add);
void IO_Expander_Write(unsigned char Data);
void LCD_Write_4Bit(unsigned char Nibble);
void LCD_CMD(unsigned char CMD);
void LCD_Set_Cursor(unsigned char ROW, unsigned char COL);
void LCD_Write_Char(char);
void LCD_Write_String(char*);
void Backlight();
void noBacklight();
void LCD_SR();
void LCD_SL();
void LCD_Clear();



/* File: I2C_LCD.c */
#include <xc.h>
#include "I2C_LCD.h"

unsigned char RS, i2c_add, BackLight_State = LCD_BACKLIGHT;

//---------------[ I2C Routines ]-------------------
//--------------------------------------------------
void I2C_Master_Init()
{
  SSPCON = 0x28;
  SSPCON2 = 0x00;
  SSPSTAT = 0x00;
  SSPADD = ((_XTAL_FREQ/4)/I2C_BaudRate) - 1;
  SCL_D = 1;
  SDA_D = 1;
}

void I2C_Master_Wait()
{
  while ((SSPSTAT & 0x04) || (SSPCON2 & 0x1F));
}

void I2C_Master_Start()
{
  I2C_Master_Wait();
  SEN = 1;
}

void I2C_Master_RepeatedStart()
{
  I2C_Master_Wait();
  RSEN = 1;
}

void I2C_Master_Stop()
{
  I2C_Master_Wait();
  PEN = 1;
}

void I2C_ACK(void)
{
  ACKDT = 0; // 0 -> ACK
  I2C_Master_Wait();
  ACKEN = 1; // Send ACK
}

void I2C_NACK(void)
{
  ACKDT = 1; // 1 -> NACK
  I2C_Master_Wait();
  ACKEN = 1; // Send NACK
}

unsigned char I2C_Master_Write(unsigned char data)
{
  I2C_Master_Wait();
  SSPBUF = data;
  while(!SSPIF); // Wait Until Completion
  SSPIF = 0;
  return ACKSTAT;
}

unsigned char I2C_Read_Byte(void)
{
  //---[ Receive & Return A Byte ]---
  I2C_Master_Wait();
  RCEN = 1; // Enable & Start Reception
  while(!SSPIF); // Wait Until Completion
  SSPIF = 0; // Clear The Interrupt Flag Bit
  I2C_Master_Wait();
  return SSPBUF; // Return The Received Byte
}
//======================================================

//---------------[ LCD Routines ]----------------
//-----------------------------------------------

void LCD_Init(unsigned char I2C_Add)
{
  i2c_add = I2C_Add;
  IO_Expander_Write(0x00);
  __delay_ms(30);
  LCD_CMD(0x03);
  __delay_ms(5);
  LCD_CMD(0x03);
  __delay_ms(5);
  LCD_CMD(0x03);
  __delay_ms(5);
  LCD_CMD(LCD_RETURN_HOME);
  __delay_ms(5);
  LCD_CMD(0x20 | (LCD_TYPE << 2));
  __delay_ms(50);
  LCD_CMD(LCD_TURN_ON);
  __delay_ms(50);
  LCD_CMD(LCD_CLEAR);
  __delay_ms(50);
  LCD_CMD(LCD_ENTRY_MODE_SET | LCD_RETURN_HOME);
  __delay_ms(50);
}

void IO_Expander_Write(unsigned char Data)
{
  I2C_Master_Start();
  I2C_Master_Write(i2c_add);
  I2C_Master_Write(Data | BackLight_State);
  I2C_Master_Stop();
}

void LCD_Write_4Bit(unsigned char Nibble)
{
  // Get The RS Value To LSB OF Data
  Nibble |= RS;
  IO_Expander_Write(Nibble | 0x04);
  IO_Expander_Write(Nibble & 0xFB);
  __delay_us(50);
}

void LCD_CMD(unsigned char CMD)
{
  RS = 0; // Command Register Select
  LCD_Write_4Bit(CMD & 0xF0);
  LCD_Write_4Bit((CMD << 4) & 0xF0);
}

void LCD_Write_Char(char Data)
{
  RS = 1; // Data Register Select
  LCD_Write_4Bit(Data & 0xF0);
  LCD_Write_4Bit((Data << 4) & 0xF0);
}

void LCD_Write_String(char* Str)
{
  for(int i=0; Str[i]!='\0'; i++)
    LCD_Write_Char(Str[i]);
}

void LCD_Set_Cursor(unsigned char ROW, unsigned char COL)
{
  switch(ROW)
  {
    case 2:
      LCD_CMD(0xC0 + COL-1);
      break;
    case 3:
      LCD_CMD(0x94 + COL-1);
      break;
    case 4:
      LCD_CMD(0xD4 + COL-1);
      break;
    // Case 1
    default:
      LCD_CMD(0x80 + COL-1);
  }
}

void Backlight()
{
  BackLight_State = LCD_BACKLIGHT;
  IO_Expander_Write(0);
}

void noBacklight()
{
  BackLight_State = LCD_NOBACKLIGHT;
  IO_Expander_Write(0);
}

void LCD_SL()
{
  LCD_CMD(0x18);
  __delay_us(40);
}

void LCD_SR()
{
  LCD_CMD(0x1C);
  __delay_us(40);
}

void LCD_Clear()
{
  LCD_CMD(0x01);
  __delay_us(40);
}



/* File: main.c */
#include <xc.h>
#include "config.h"
#include "I2C_LCD.h"

void main(void) {

  I2C_Master_Init();
  LCD_Init(0x27); // Initialize LCD module with I2C address = 0x4E

  LCD_Set_Cursor(1, 1);
  LCD_Write_String(" Khaled Magdy");
  LCD_Set_Cursor(2, 1);
  LCD_Write_String(" DeepBlue");

  while(1)
  {
    LCD_SR();
    __delay_ms(350);
    LCD_SR();
    __delay_ms(350);
    LCD_SL();
    __delay_ms(350);
    LCD_SL();
    __delay_ms(350);
  }
  return;
}

希望能从不同的角度关注此事,因为我们不知疲倦地工作,并且不确定为什么没有沟通。

c microcontroller i2c pic
1个回答
0
投票

与 PIC 微控制器相比,字符 LCD 模块是非常慢的设备。因此,他们处理命令和数据所需的时间可能会根据具体产品而有所不同。

这些模块应通过尊重其处理时间来正确设置,以使它们正常运行。这就是为什么

init
程序对于这些设备非常重要,应该小心处理。

作为参考,这里是取自工作项目的示例 init 函数代码片段。

// Init the LCD module for 4-bit mode
void lcd4bitInit(void)
{
    __delay_ms(100);    // Character LCD modules are relatively slow, the required time may vary depending on the product
                        // So give it sufficient time especially when setting up
    lcdPort = 0x30;     // Send function set command 3 times with 10ms intervals
    lcdRW_low();        // Set LCD RW low for write mode
    lcdRS_low();        // Set LCD RS low for command mode
    lcdEN_high();       // Set EN low so that it latch the data in, should keep high at least 140ns
    lcdEN_low();        // Set EN low again
    __delay_ms(10);     // Let it process the data
    lcdEN_high();       // Latch the function set command for 2nd time (still 8-bit mode).
    lcdEN_low();
    __delay_ms(10);     // Let it process the data
    lcdEN_high();       // Latch the function set command for 3d time (still 8-bit mode).
    lcdEN_low();
    __delay_ms(10);     // Let it process the data
    
    lcdPort = 0x28;     // Set DL 0: 4-bits; N 1: 2 lines; F 0: 5x7 dots
    lcdEN_high();
    lcdEN_low();
    __delay_ms(10);
    /* From now on, the LCD module should function in 4-bit mode. So we can write it in 4-bit mode as of now */
    
    lcdCmd_write(0x08); // Here goes the rest of the setup which can be sent in 4-bit mode
    lcd_clear();
    lcdCmd_write(0x06);
    lcdCmd_write(0x0C);
}

代码片段中的引脚设置函数是类似函数的宏。例如

lcdEN_high()
和 `lcdEN_low 的定义分别如下:

#define lcdEN_high()      do {lcdENPort |= 1 << lcdEN; } while(0)
#define lcdEN_low()       do {lcdENPort &= ~(1 << lcdEN); } while(0)

其中

lcdENPort
映射到我控制
EN
引脚的引脚端口,
lcdEN
映射到连接LCD的
EN
的位数。

正确设置的另一个重要因素是尊重模块的处理时间。正如我之前所说,这个时间在不同的产品或制造商之间可能会略有不同。但我认为保持这些时间比所需时间多一点没有问题,因为

init
函数通常在每个系统初始化时执行一次。

© www.soinside.com 2019 - 2024. All rights reserved.