14.7 I2C和EEPROM的综合编程

电视频道记忆功能,交通灯倒计时时间的设定,户外 LED 广告的记忆功能,都有可能用到 EEPROM 这类存储器件。这类器件的优势是存储的数据不仅可以改变,而且掉电后数据保存不丢失,因此大量应用在各种电子产品上。

我们这节课的例程,有点类似广告屏。上电后,1602 的第一行显示 EEPROM 从 0x20 地址开始的 16 个字符,第二行显示 EERPOM 从 0x40 开始的 16 个字符。我们可以通过 UART串口通信来改变 EEPROM 内部的这个数据,并且同时也改变了 1602 显示的内容,下次上电的时候,直接会显示我们更新过的内容。

这个程序所有的相关内容,前面都已经讲过了。但是这个程序体现在了一个综合应用能力上。这个程序用到了 1602 液晶、UART 串口通信、EEPROM 读写操作等多个功能的综合应用。写个点亮小灯好简单,但是我们想真正学好单片机,必须得学会这种综合程序的应用,实现多个模块同时参与工作。因此同学们,要认认真真的把工程建立起来,一行一行的把程序编写起来,最终巩固下来。

/*****************************I2C.c 文件程序源代码*******************************/
(此处省略,可参考之前章节的代码)
/***************************Lcd1602.c 文件程序源代码*****************************/
(此处省略,可参考之前章节的代码)
/****************************eeprom.c 文件程序源代码*****************************/
(此处省略,可参考之前章节的代码)
/*****************************Uart.c 文件程序源代码*****************************/
(此处省略,可参考之前章节的代码)
    
    
    
    
  1. /*****************************main.c 文件程序源代码******************************/
  2. #include <reg52.h>
  3. unsigned char T0RH = 0; //T0 重载值的高字节
  4. unsigned char T0RL = 0; //T0 重载值的低字节
  5. void InitShowStr();
  6. void ConfigTimer0(unsigned int ms);
  7. extern void InitLcd1602();
  8. extern void LcdShowStr(unsigned char x, unsigned char y, unsigned char *str);
  9. extern void E2Read(unsigned char *buf, unsigned char addr, unsigned char len);
  10. extern void E2Write(unsigned char *buf, unsigned char addr, unsigned char len);
  11. extern void UartDriver();
  12. extern void ConfigUART(unsigned int baud);
  13. extern void UartRxMonitor(unsigned char ms);
  14. extern void UartWrite(unsigned char *buf, unsigned char len);
  15. void main(){
  16. EA = 1; //开总中断
  17. ConfigTimer0(1); //配置 T0 定时 1ms
  18. ConfigUART(9600); //配置波特率为 9600
  19. InitLcd1602(); //初始化液晶
  20. InitShowStr(); //初始显示内容
  21. while (1){
  22. UartDriver(); //调用串口驱动
  23. }
  24. }
  25. /* 处理液晶屏初始显示内容 */
  26. void InitShowStr(){
  27. unsigned char str[17];
  28. str[16] = '\0';//在最后添加字符串结束符,确保字符串可以结束
  29. E2Read(str, 0x20, 16); //读取第一行字符串,其 E2 起始地址为 0x20
  30. LcdShowStr(0, 0, str); //显示到液晶屏
  31. E2Read(str, 0x40, 16); //读取第二行字符串,其 E2 起始地址为 0x40
  32. LcdShowStr(0, 1, str); //显示到液晶屏
  33. }
  34. /* 内存比较函数,比较两个指针所指向的内存数据是否相同,
  35. ptr1-待比较指针 1,ptr2-待比较指针 2,len-待比较长度
  36. 返回值-两段内存数据完全相同时返回 1,不同返回 0 */
  37. bit CmpMemory(unsigned char *ptr1, unsigned char *ptr2, unsigned char len){
  38. while (len--){
  39. if (*ptr1++ != *ptr2++){ //遇到不相等数据时即刻返回 0
  40. return 0;
  41. }
  42. }
  43. return 1; //比较完全部长度数据都相等则返回 1
  44. }
  45. /* 将一字符串整理成 16 字节的固定长度字符串,不足部分补空格
  46. out-整理后的字符串输出指针,in-待整理字符串指针 */
  47. void TrimString16(unsigned char *out, unsigned char *in){
  48. unsigned char i = 0;
  49. while (*in != '\0'){ //拷贝字符串直到输入字符串结束
  50. *out++ = *in++;
  51. i++;
  52. if (i >= 16){ //当拷贝长度已达到 16 字节时,强制跳出循环
  53. break;
  54. }
  55. }
  56. for ( ; i<16; i++){ //如不足 16 个字节则用空格补齐
  57. *out++ = ' ';
  58. }
  59. *out = '\0'; //最后添加结束符
  60. }
  61. /* 串口动作函数,根据接收到的命令帧执行响应的动作
  62. buf-接收到的命令帧指针,len-命令帧长度 */
  63. void UartAction(unsigned char *buf, unsigned char len){
  64. unsigned char i;
  65. unsigned char str[17];
  66. unsigned char code cmd0[] = "showstr1 "; //第一行字符显示命令
  67. unsigned char code cmd1[] = "showstr2 "; //第二行字符显示命令
  68. unsigned char code cmdLen[] = { //命令长度汇总表
  69. sizeof(cmd0)-1, sizeof(cmd1)-1,
  70. };
  71. unsigned char code *cmdPtr[] = { //命令指针汇总表
  72. &cmd0[0], &cmd1[0],
  73. };
  74. for (i=0; i<sizeof(cmdLen); i++){ //遍历命令列表,查找相同命令
  75. if (len >= cmdLen[i]){ //首先接收到的数据长度要不小于命令长度
  76. if (CmpMemory(buf, cmdPtr[i], cmdLen[i])){ //比较相同时退出循环
  77. break;
  78. }
  79. }
  80. }
  81. switch (i){ //根据比较结果执行相应命令
  82. case 0:
  83. buf[len] = '\0'; //为接收到的字符串添加结束符
  84. TrimString16(str, buf+cmdLen[0]); //整理成 16 字节固定长度字符串
  85. LcdShowStr(0, 0, str); //显示字符串 1
  86. E2Write(str, 0x20, sizeof(str)); //保存字符串 1,起始地址为 0x20
  87. break;
  88. case 1:
  89. buf[len] = '\0'; //为接收到的字符串添加结束符
  90. TrimString16(str, buf+cmdLen[1]); //整理成 16 字节固定长度字符串
  91. LcdShowStr(0, 1, str); //显示字符串 1
  92. E2Write(str, 0x40, sizeof(str)); //保存字符串 2,起始地址为 0x40
  93. break;
  94. default: //未找到相符命令时,给上机发送“错误命令”的提示
  95. UartWrite("bad command.\r\n", sizeof("bad command.\r\n")-1);
  96. return;
  97. }
  98. buf[len++] = '\r'; //有效命令被执行后,在原命令帧之后添加
  99. buf[len++] = '\n'; //回车换行符后返回给上位机,表示已执行
  100. UartWrite(buf, len);
  101. }
  102. /* 配置并启动 T0,ms-T0 定时时间 */
  103. void ConfigTimer0(unsigned int ms){
  104. unsigned long tmp; //临时变量
  105. tmp = 11059200 / 12; //定时器计数频率
  106. tmp = (tmp * ms) / 1000; //计算所需的计数值
  107. tmp = 65536 - tmp; //计算定时器重载值
  108. tmp = tmp + 33; //补偿中断响应延时造成的误差
  109. T0RH = (unsigned char)(tmp>>8); //定时器重载值拆分为高低字节
  110. T0RL = (unsigned char)tmp;
  111. TMOD &= 0xF0; //清零 T0 的控制位
  112. TMOD |= 0x01; //配置 T0 为模式 1
  113. TH0 = T0RH; //加载 T0 重载值
  114. TL0 = T0RL;
  115. ET0 = 1; //使能 T0 中断
  116. TR0 = 1; //启动 T0
  117. }
  118. /* T0 中断服务函数,执行串口接收监控和蜂鸣器驱动 */
  119. void InterruptTimer0() interrupt 1{
  120. TH0 = T0RH; //重新加载重载值
  121. TL0 = T0RL;
  122. UartRxMonitor(1); //串口接收监控
  123. }
我们在学习 UART 通信的时候,刚开始也是用的 IO 口去模拟 UART 通信过程,最终实现和电脑的通信,而后因为 STC89C52 内部具备 UART 硬件通信模块,所以我们直接可以通过配置寄存器就可以很轻松的实现单片机的 UART 通信。同样的道理,这个 I 2 C 通信,如果单片机内部有硬件模块的话,单片机可以直接自动实现 I 2 C 通信了,就不需要我们再进行 IO口模拟起始、模拟发送、模拟结束,配置好寄存器,单片机就会把这些工作全部做了。

不过我们的 STC89C52 单片机内部不具备 I 2 C 的硬件模块,所以我们使用 STC89C52 进行 I 2 C 通信的话必须用 IO 口来模拟。使用 IO 口模拟 I 2 C 实际上更有利于我们彻底理解透彻 I 2 C 通信的实质。当然了,通过学习 IO 口模拟通信,今后如果遇到内部带 I 2 C 模块的单片机,也应该很轻松的搞定,使用内部的硬件模块,可以提高程序的执行效率。

你可能感兴趣的:(14.7 I2C和EEPROM的综合编程)