这些小活动你都参加了吗?快来围观一下吧!>>
电子产品世界 » 论坛首页 » 嵌入式开发 » 国产MCU » 芯圣HC18M003单片机驱动IICLCD1602的例程

共2条 1/1 1 跳转至

芯圣HC18M003单片机驱动IICLCD1602的例程

专家
2023-11-01 09:01:30   被打赏 50 分(兑奖)     打赏

  折腾了近3天,终于搞通了使用HC18M003单片机驱动IIC LCD1602的例程。中间经历了太多的波折,调试也是相当花时间和精力,好在终于调通了。感觉使用IIC外设固然是一种选择,但好像也没有那么令人心动。和自己预想的那种,只需要把数据扔过去啥都不用管的模式不一样啊。需要及时响应中断,以及判断数据传输状态。否则就有可能出错。有机会再试试用DMA方式和IIC外设交互,看看是不是能省事儿点吧。

  以下是驱动例程:

/**
*   模块性能介绍 
*   1、双线通讯,支持主机以及从机模式 
*   2、支持多主机通讯时钟仲裁功能 
*   3、支持地址可编程 
*   4、支持标准速率(最多100kbps)和快速(最多400kbps) 
*    
*   以IIC方式驱动LCD1602,无视中断时IIC设备的状态值,只是按顺序发送数据 
*    ************************************************************************************ 
*                                   代码配置注意事项 
*     1、当CPU运行在8M时,BOR必须使能为4.2V; 
*     2、当CPU运行在4M时,BOR必须使能为3.0V及以上; 
*     3、当CPU运行在2M时,BOR必须使能为2.0V及以上,常规情况下不建议关闭BOR; 
*     4、使能BOR时除程序中修改,建议OPTION也配置相同电压。 
*   ************************************************************************************ 
* LCD1602模块:  VCC  - 5V 
*  (PCF8547)     GND 
*                SCL - P3.2 
*                SDA - P3.3 
* PCF8574T      LCD1602 
*================================================================================ 
* P7              DB7 
* P6              DB6 
* P5              DB5 
* P4              DB4 
* P3             控制背光灯 
* P0              RS 
* P1              RW 
* P2              CS 
*================================================================================ 
* LCD1602的操作 
* 写指令: RS=0, RW=0, CS(E)上升沿 
* 写数据: RS=1, RW=0, CS(E)上升沿 
*================================================================================ 
* LCD1602的操作指令 
* Bit                   RS  RW   7   6   5   4   3   2   1   0                   
*----------------------------------------------------------------------------------- 
*  1.清除显示            0   0   0   0   0   0   0   0   0   1 
*  2.光标返回            0   0   0   0   0   0   0   0   1   * 
*  3.设输入模式          0   0   0   0   0   0   0   1   I/D S     I/D:光标移动方向,1-右移;0-左移 
*                                                                  S:屏幕上所有文字是否左移或右移,1表示有效,0表示无效 
*  4.显示开关            0   0   0   0   0   0   1   D   C   B     D=1开显示,D=0关显示 
*                                                                  C=1显示光标,C=0关闭光标 
*                                                                  B=1光标闪烁,B=0光标不闪烁 
*  5.光标字符移位        0   0   0   0   0   1   S/C R/L *   *     S/C 1-显示移动的文字,0-移动光标 
*                                                                  R/L 左右方向 
*  6.设置功能            0   0   0   0   1   DL  N   F   *   *     DL: 1-4位总线;0-8位总线 
*                                                                  N:  1-双行显示;0-单行显示 
*                                                                  F:  1-5X10点阵, 0-5x7点阵 
*  7.设置字符发生器地址  0   0   0   1   字符发生器RAM            
*  8.设置数据存储器地址  0   0   1   显示数据存储器地址 
*----------------------------------------------------------------------------------- 
*  9.读忙标志或地址      0   1   BF  计数器地址 
* 10.写数到CGRAM或DDRAM  1   0   要写的数据内容  
* 11.从CGRAM或DDRAM读    1   1   读出的数据 
*=================================================================================== 
* 数据指针设置             80H + 地址码(0-27H(第一行),40H-67H(第二行)) 
** LCD1602 四线驱动方式下,一个字节数据传输2回 
**/  
  
#include"holychip_define.h"  
 
// Debug用定义
//#define DEBUG
  
//--------------OLED参数定义---------------------  
#define LINE_1_ADDR 0x40  
#define LINE_2_ADDR 0x80  
#define WIDTH             128  
#define HEIGHT             32      
  
#define OLED_CMD     0    //写命令  
#define OLED_DATA    1    //写数据   
  
#define IIC_SLAVE_ADDR           0x4E            //IIC器件地址:  
#define LCD_ADDR_W               0x4E  
#define LCD_ADDR_R               0x4F   
  
#define IIC_STATUS_START_OK      0x08            // START发送完成后的状态(ACK应答)(STA,STO,SI,AA)=(1,0,0,X)  
#define IIC_STATUS_REPEAT_START_OK      0x10     // START发送完成后的状态(ACK应答)(STA,STO,SI,AA)=(1,0,0,X)  
#define IIC_STATUS_WADDR_ACK_OK  0x18            // 向从机发送写指令+地址,发送完成后的状态(ACK应答)(STA,STO,SI,AA)=(X,0,0,X)  
#define IIC_STATUS_DATA_ACK_OK   0x28            // 向从机发送数据,完成后的状态(ACK应答)(STA,STO,SI,AA)=(0,0,0,X)  
  
#define IIC_STATUS_WADDR_NACK_OK  0x20           // 向从机发送写指令+地址,发送完成后的状态(NACK应答)(STA,STO,SI,AA)=(X,0,0,X)  
#define IIC_STATUS_DATA_NACK_OK   0x30           // 向从机发送数据,完成后的状态(NACK应答)(STA,STO,SI,AA)=(0,0,0,X)  
  
//unsigned char HEXCHR[16]={'0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'};  
  
unsigned int   gms=0;                     // 延时变量  
  
/*************************************************************************************** 
 * @说明     1毫秒单位的延时 
 * @参数     ms 毫秒数 
 * @返回值   无 
 * @注       比较准确, 
***************************************************************************************/  
void delay_ms(unsigned int ms) {  
    gms=ms;  
    while(gms > 0) {  
        // 等待    gms = 0  
    }  
}  
  
/*************************************************************************************** 
 * @说明      10微秒单位的延时 
 * @参数      ms 毫秒数 
 * @返回值    无 
 * @注        不准确, 
***************************************************************************************/  
void delay_10us(unsigned int wm) {   
    unsigned char a,b,c;  
    for(c=0; c<wm; c++) {  
        for(b=0; b<40; b++) {  
        }  
    }  
}  
  
/*************************************************************************************** 
 * @说明    取得十六进制字符 
 * @参数    chr 字符 
 *          h_l_flag 0 - 低四位对应16进制字符;1 - 高四位对应字符 
 * @返回值  无 
 * @注      无 
***************************************************************************************/  
unsigned char toHexChar(unsigned char chr, unsigned char h_l_flag) {  
    unsigned char val = chr;  
    unsigned char result=0;  
      
    if (h_l_flag) {  
        val = (val & 0xF0)>>4;  
    } else {  
        val = val & 0x0f;  
    }  
    
  switch (val) {  
    case 0:  
        result = '0';break;  
    case 1:  
        result = '1';break;  
    case 2:  
        result = '2';break;  
    case 3:  
        result = '3';break;  
    case 4:  
        result = '4';break;  
    case 5:  
        result = '5';break;  
    case 6:  
        result = '6';break;  
    case 7:  
        result = '7';break;  
    case 8:  
        result = '8';break;  
    case 9:  
        result = '9';break;  
    case 10:  
        result = 'A';break;  
    case 11:  
        result = 'B';break;  
    case 12:  
        result = 'C';break;  
    case 13:  
        result = 'D';break;  
    case 14:  
        result = 'E';break;  
    case 15:  
        result = 'F';break;  
        default:  
            result = '-';break;  
    }  
    return result;  
}  
  
/*************************************************************************************** 
 * @说明      判断字符是否能显示 
 * @参数      chr 
 * @返回值    0 - 不可显示; 1 - 可显示  
 * @注         
***************************************************************************************/  
unsigned char isDispChar(unsigned char chr) {   
    if (chr >= 32 && chr <= 126) {  
        return 1;  
    }  
    return 0;  
}  
  
/*************************************************************************************** 
 * @说明    串口发送一个字符 
 * @参数    chr 字符 
 * @返回值  无 
 * @注      无 
***************************************************************************************/  
void send_char(unsigned char chr) {  
    SBUF = chr;         //发送8位串口数据  
    while(!TXIF);  
    TXIF = 0;        //清除发送中断标志位  
}  
  
/*************************************************************************************** 
 * @说明    串口发送一个字符,以十六进制方式表示 
 * @参数    chr 字符 
 * @返回值  无 
 * @注      无 
***************************************************************************************/  
void send_char_hex(unsigned char chr) {  
    SBUF = '0';  
    while(!TXIF);  
    TXIF = 0;        //清除发送中断标志位  
  
    SBUF = 'x';  
    while(!TXIF);  
    TXIF = 0;        //清除发送中断标志位  
  
    SBUF = toHexChar(chr, 1);  
    while(!TXIF);  
    TXIF = 0;        //清除发送中断标志位  
      
    SBUF = toHexChar(chr, 0);  
    while(!TXIF);  
    TXIF = 0;        //清除发送中断标志位  
  
    SBUF = ' ';  
    while(!TXIF);  
    TXIF = 0;        //清除发送中断标志位  
}  
  
/*************************************************************************************** 
 * @说明    串口发送一个字符,以十进制方式表示 
 * @参数    chr 字符 
 * @返回值  无 
 * @注      无 
***************************************************************************************/  
void send_char_dec(unsigned char chr) {  
    if (chr > 99) {  
      SBUF = toHexChar(chr/100, 0);  
      while(!TXIF);  
      TXIF = 0;        //清除发送中断标志位  
    }  
      
    if (chr > 9) {  
      SBUF = toHexChar((chr%100)/10, 0);  
      while(!TXIF);  
      TXIF = 0;        //清除发送中断标志位  
    }  
  
    SBUF = toHexChar(chr%10, 0);  
    while(!TXIF);  
    TXIF = 0;        //清除发送中断标志位  
}  
  
  
/*************************************************************************************** 
 * @说明    串口发送一个字符串 
 * @参数    str 字符串 
 * @返回值  无 
 * @注      无 
***************************************************************************************/  
void send_str(unsigned char *str) {  
    unsigned char chr = '\0';  
    while (chr=*str) {  
        send_char(chr);  
        str++;  
    }  
}  
  
/*************************************************************************************** 
 * @说明    中断服务函数 
 * @参数    无 
 * @返回值  无 
 * @注      包含定时器2中断和IIC中断 
***************************************************************************************/  
void interrupt all_isr(void) {  
     
    // IIC中断  
    if (IICIF) {  
       PORTA0 = ~PORTA0;
    }  
      
//    // 串口接收中断,收到的数据存入数据缓冲区(对应两行32个字符)  
//    if(RXIF) {  
//        guc_Uartbuf  = SBUF;        // 转存8位串口接收数据  
//        guc_Uartflag = 1;           // 接收中断标志  
//          
//        // 如果是可显示的字符,保存  
//        if (isDispChar(guc_Uartbuf)) {  
//          guc_lcdchar[lcdchar_addr] = guc_Uartbuf;  
//          lcdchar_addr = (lcdchar_addr+1)%32;             // 显示位置指向下一个   
//        }  
//          
//        SCON &= ~0x10;              //禁止串口继续继续接收数据  
//        RXIF  = 0;                  //清除接收中断标志位  
//    }      
      
   // 定时器0中断  
   if(T0IF) {  
          T0  = 0xF0;            //T0定时时间1ms  
       if (gms>0) {  
           gms=gms-1;  
       }  
       T0IF   = 0;            //清除T0中断标志位  
   }      
}  
  
/*************************************************************************************** 
 * @说明      通过IIC发送一个字节给PCF8574T,PCF8574T是作为IIC从机存在的 
 * @参数      dat 呈现到 P7 ~ P0管脚上,连接到LCD引脚 
 *                P7  --  LCD160-DB7 
 *                P6  --  LCD160-DB6 
 *                P5  --  LCD160-DB5 
 *                P4  --  LCD160-DB4 
 *                P3  --  LCD1602-背光灯 
 *                P2  --  LCD1602-E 
 *                P1  --  LCD1602-RW 
 *                P0  --  LCD1602-RS 
 * @返回值    无 
 * @注        发送给LCD1602,要通过PCF8574T实现 
***************************************************************************************/  
unsigned char send_pcf8574Byte(unsigned char dat) {
    unsigned char iic_status = 0; // 每次发送完成后获得的IIC状态码
    unsigned char result = 0;  // 返回值
    //send_str((unsigned char *)"\r\n<send_pcf8574Byte>");  
    
    // 清除IIC中断标志  
    IICIF = 0;  
    //启动IIC模块
    IICCON |= 0x40;
    delay_10us(1);
                                  
    // 使IIC外设向IIC总线发送“START”信号  
    IICCON |= 0x20;       // 产生开始信号  
#ifdef DEBUG
    send_char_hex(0xA1);  
#endif
    // 等待中断发生  
    while (!IICIF);  
    iic_status=IICSTA;
#ifdef DEBUG
    send_char_hex(iic_status);  
#endif    
      
    if(iic_status == 0x08) {  
#ifdef DEBUG
      send_char_hex(0xA2);  
#endif
      // 发送子机地址    
      IICCON &=~ 0x20;       // STA=0,禁止继续发START信号  
      IICCON |= 0x04;        // AA=1,确认子机回复ACK信号?  
      IICDAT = IIC_SLAVE_ADDR;  
      IICIF = 0;  
    } else {
#ifdef DEBUG    
      send_char_hex(0xA0);  
#endif
      result = 1;  
      goto send_pcf8574Byte_end;
    }  
      
    // 等待中断发生  
    while (!IICIF);  
    iic_status=IICSTA;  
 
#ifdef DEBUG
    send_char_hex(iic_status);  
#endif
      
    if(iic_status == 0x18) {
      
#ifdef DEBUG
      send_char_hex(0xA3);  
#endif      
      // 发送数据  
      IICCON &=~ 0x20;       // STA=0,禁止继续发START信号  
      IICCON |= 0x04;        // AA=1,确认子机回复ACK信号?  
      IICDAT = dat;  
      IICIF = 0;  
    } else {
      
#ifdef DEBUG    
      send_char_hex(0xA0);  
#endif      
      result = 2;
      goto send_pcf8574Byte_end;
    }  
      
      
    // 等待中断发生  
    while (!IICIF);  
    iic_status=IICSTA;
    
#ifdef DEBUG      
    send_char_hex(iic_status);
#endif    
    if(iic_status == 0x28) {
    
#ifdef DEBUG    
      send_char_hex(0xA9);
#endif      
    } else {
    
#ifdef DEBUG    
      send_char_hex(0xA0);
#endif
      result = 3;
      goto send_pcf8574Byte_end;
    }
     
    IICCON |= 0x10;      // 停止信号
    IICCON &= ~0x40;     // 关闭IIC
    return 0;
 
send_pcf8574Byte_end:
IICCON |= 0x10;      // 停止信号
    delay_ms(1);
    IICCON &= ~0x40;     // 关闭IIC
    delay_ms(1);
    return result;
}  
  
/*************************************************************************************** 
 * @说明      向LCD1602发送指令 
 * @参数      cmd 指令 
 * @返回值    无 
 * @注        通过PCF8574T转成引脚输出,适配LCD1602方式 
 * ----------------------------------- 
 * dat    PCF8574T          LCD1602 
 *------------------------------------ 
 * D7     P7              DB7 
 * D6     P6              DB6 
 * D5     P5              DB5 
 * D4     P4              DB4 
 * D3     P3             控制背光灯 
 * D2     P0              RS 
 * D1     P1              RW 
 * D0     P2              E  
***************************************************************************************/  
void write_lcd1602_cmd(unsigned char cmd) {  
    unsigned char cmd1, cmd2;  
    //send_str((unsigned char *)"\r\n<write_lcd1602_cmd>");
    
    cmd1=cmd|0x0f;  
    // 发送高4位  
    send_pcf8574Byte(cmd1 & 0xfc);    // 11111100 : CS=1,RW=0, RS=0  
    delay_10us(1);  
    send_pcf8574Byte(cmd1 & 0xf8);    // 11111000 : CS=0,RW=0, RS=0  
      
    // 发送低4位,要移位到高4位位置上  
    cmd2=cmd<<4;  
    cmd2=cmd2|0x0f;  
    send_pcf8574Byte(cmd2 & 0xfc);    // 11111100 : CS=1,RW=0, RS=0  
    delay_10us(1);  
    send_pcf8574Byte(cmd2 & 0xf8);    // 11111000 : CS=0,RW=0, RS=0  
}  
  
/*************************************************************************************** 
 * @说明      向LCD1602发送数据 
 * @参数      dat 指令 
 * @返回值    无 
 * @注        通过PCF8574T转成引脚输出,适配LCD1602方式 
 * ----------------------------------- 
 * dat    PCF8574T          LCD1602 
 *------------------------------------ 
 * D7     P7              DB7 
 * D6     P6              DB6 
 * D5     P5              DB5 
 * D4     P4              DB4 
 * D3     P3             控制背光灯 
 * D2     P0              RS 
 * D1     P1              RW 
 * D0     P2              E  
***************************************************************************************/  
void write_lcd1602_data(unsigned char dat) {  
    unsigned char dat1, dat2;
//    send_str((unsigned char *)"\r\n<write_lcd1602_data>"); 
      
    dat1=dat|0x0f;  
    // 发送高4位  
    send_pcf8574Byte(dat1 & 0xfd);    // 11111101 : CS=1,RW=0, RS=1  
    delay_10us(1);  
    send_pcf8574Byte(dat1 & 0xf9);    // 11111001 : CS=0,RW=0, RS=1  
      
    // 发送低4位,要移位到高4位位置上  
    dat2=dat<<4;  
    dat2=dat2|0x0f;  
    send_pcf8574Byte(dat2 & 0xfd);    // 11111101 : CS=1,RW=0, RS=1  
    delay_10us(1);  
    send_pcf8574Byte(dat2 & 0xf9);    // 11111001 : CS=0,RW=0, RS=1  
}  
  
/*************************************************************************************** 
 * @说明      初始化LCD 
 * @参数      无 
 * @返回值    无 
 * @注        无 
***************************************************************************************/  
void lcd_init(void) {  
//    send_str((unsigned char *)"\r\n<lcd_init>");  
//    write_lcd1602_cmd(0x38);    // 设置显示 4 (0x38:8)
//    delay_ms(5);      
    write_lcd1602_cmd(0x33);    // 设置显示 4 (0x38:8)
    delay_ms(5);  
    write_lcd1602_cmd(0x32);    // 设置4线控制  
    delay_ms(5);
    write_lcd1602_cmd(0x28);    // 设置16*2, 5*7, 4线初始化  
    delay_ms(5);
    write_lcd1602_cmd(0x06);    // 地址加1,数据不移动,地址移动  
    delay_ms(5);
    write_lcd1602_cmd(0x0C);    // 不显示光标,光标不闪烁  
    delay_ms(5);
//    write_lcd1602_cmd(0x0f);    //  显示光标,光标闪烁  
//    delay_ms(5);
    write_lcd1602_cmd(0x01);    // 清屏  
    delay_ms(5);
    write_lcd1602_cmd(0x80);    // 起始地址  
    delay_ms(100);      
}  
  
/*************************************************************************************** 
 * @说明      设置显示位置 
 * @参数      row 垂直位置(0~1) 
 *            col 水平位置(0~15) 
 * @返回值    无 
 * @注        无 
***************************************************************************************/  
void lcd1602_GotoXY(unsigned char row, unsigned char col) {  
//    send_str((unsigned char *)"\r\n<lcd1602_GotoXY>row=");  
//    send_char_dec(row);  
//    send_str((unsigned char *)",col=");  
//    send_char_dec(col);  
      
    if(row == 0) write_lcd1602_cmd(0x80 + col);  
    if(row == 1) write_lcd1602_cmd(0x80 + 0x40 + col);  
}  
  
  
  
/*************************************************************************************** 
 * @说明      使LCD在当前位置显示字符串 
 * @参数      str 字符串 
 * @返回值    无 
 * @注        无 
***************************************************************************************/  
void LCD1602_Display_NoXY(unsigned char *str) {  
    //send_str((unsigned char *)"\r\n<LCD1602_Display_NoXY>str=");  
    //send_str(str);  
      
    while(*str != '\0') {  
        write_lcd1602_data(*str);  
        str++;  
    }  
}  
  
  
/*************************************************************************************** 
  * @实现效果    IIC主机发送与接收 
***************************************************************************************/  
void main() {  
    //unsigned char arry[]="This is test!";  
    //unsigned char old_ptr = 0;  // LCD显示字符位置  
     unsigned char sta=0;  
      
    /*************************系统初始化*************************/  
    OSCCON  = 0x04;                               //Fosc=32M Fcpu=4M(Fosc4分频 2T)      
    /*************************IO初始化***************************/  
    ANSELA  = 0x01;    //PA0设为数字形式  
    TRISA  = 0x01;    //PA0为输出口-中断指示  
  
    ANSELC  = 0xFF;                               //PC口的B0、B1、B2、B4、B6、B7为数字模式,对TSSOP20封装,只有PC0,PC1,PC2,PC4  
    // 对TSSOP20封装,PORTC2、PORTD4、PORTB6、可对应于IIC_SDA;  
    //                PORTC2和PORTB7可对应于IIC_SCK;  
    // 此处使用PORTC2和PORTC0映射为IIC的SCK和SDA  
    IICMAP  = 0x11;  
    // PORTC2为输出模式  
    //TRISC   = 0x04;  
    TRISC   = 0x05;    // PC2和PC0输出模式  
        
        /*************************IIC初始化*****************************/  
    // IICCON构成:CR2\IICEN\STA\STO\-\AA\CR1\CR0  
    // 0x40: CR2~CR0:000,Fcpu=4M模式下,通讯速度=15.63KHz  
    //       IICEN=1,启动IIC模块  
    //       STA=0,不发送起始信号  
    //       STO=0,不发送停止信号  
    //       AA=1,回复ACK(SDA上为低电平)  
    IICCON  = 0x44;                               //启动IIC模块           
      
    /********************T0配置初始化********************/  
    OPTION  = 0X07;               // 分频寄存器配置256分频  
    T0CS    = 0;                  // T0 模式选择寄存器:定时器模式,计数时钟Fcpu,休眠和绿色模式下停止  
    T0OSCEN = 0;                  // 禁止定时器模块0使用计数时钟  
    T0SE    = 0;                  // 定时器模式,计数时钟Fcpu  
  
  
    /********************UART配置初始化********************/  
    ANSELB = 0xC0;                  //PB7 PB6设为数字模式  
    TRISB  = 0x40;                  //PB7设为输入模式 PB6设为输出模式  
  
    INTMAP = 0x20;  
    UARTMAP = 0x50;                 //映射 PB7口作为RX PB6口作为TX  
    // 波特率等设置  
    BRTH   = 0xFF;          
    BRTL   = 0xE6;                  //Baudrate_9600  
    SCON2  = 0x24;                  //UART功能选择 8位  
    SCON  |= 0x10;                  //使能UART接收  
        
  
    /********************中断设置********************/  
    T0IE = 1;        //打开T0中断   
    //IICIE = 1;       //允许IIC中断  
    PEIE = 1;        //允许未屏蔽中断  
    GIE = 1;         //允许总中断  
  
    send_str((unsigned char *)"\r\n<main>");  
    
    // 关闭中断指示LED  
    PORTA0 = 1;  
    
    // 初始化LCD
    lcd_init();
   
    // 在第一行第一列显示字符A
    lcd1602_GotoXY(0,0);
    write_lcd1602_data('A');
    
    // 在第二行第一列显示字符串:Test OK!
    lcd1602_GotoXY(1,0);
    LCD1602_Display_NoXY("Test OK!");    
    while(1) {
 
    }  
}

微信图片_20231101090226.jpg




关键词: 菜鸟学单片机    

院士
2023-11-01 09:25:12     打赏
2楼

您这实验平台的硬件环境还是真有想法!

I2C的通讯还是建议使用状态机的设计模式


共2条 1/1 1 跳转至

回复

匿名不能发帖!请先 [ 登陆 注册 ]