[STM8] 基于IAR开发环境的STM8S模拟IIC代码

[复制链接]
 楼主| owenli520 发表于 2015-1-25 14:12 | 显示全部楼层 |阅读模式
本帖最后由 owenli520 于 2015-1-25 14:30 编辑

基于IAR STM8 2.10开发环境,STM8S005K6单片机,模拟IIC方式读写AT24C16或AT24C32,已验证可以正常对AT24C32进行读写数据,只是有个疑问,对于单片机接SDA脚的IO口进行方向控制反而引起时序不正常而读写不了数据,例程中是将此IO口始终设置为输出,不知道会不会有隐患,有待测试...  
以下是代码,完整代码请下载附件,包含完整工程!
  1. #include "24Cxx.h"
  2. #include "stm8s.h"
  3. #include "stm8s_gpio.h"


  4. #define AT24C32_SETSDAIN  GPIO_Init(AT24C32_SDA_PORT, GPIO_PIN_6, GPIO_MODE_IN_FL_NO_IT)
  5. #define AT24C32_SETSDAOUT  GPIO_Init(AT24C32_SDA_PORT, GPIO_PIN_6, GPIO_MODE_OUT_OD_LOW_SLOW)

  6. #define I2C_SLAW 0xA0        /*器件地址选择及写标志*/
  7. #define I2C_SLAR 0xA1        /*器件地址选择及读标志*/


  8. void nops(void)
  9. {
  10. //        asm("nop");
  11. //        asm("nop");
  12.         uint16_t i = 0;
  13.         for (; i < 5; i++);
  14. }


  15. // 总线复位
  16. void I2C_Reset(void)
  17. {
  18.         AT24C32_SCL_HIGH;
  19.         AT24C32_SDA_HIGH;
  20. }

  21. // I2C操作严重错误
  22. void I2C_Error(void)
  23. {
  24.         //复位I2C总线
  25.         I2C_Reset();
  26.         
  27.         //进入死循环
  28.         while (1)
  29.         {
  30.         }
  31. }

  32. // 发送起始条件
  33. void I2C_Start_A(void) /*起始条件*/
  34. {
  35.         AT24C32_SCL_HIGH;
  36.         nops();

  37.         AT24C32_SDA_LOW;
  38.         nops();

  39.         AT24C32_SCL_LOW;
  40.         nops();
  41. }

  42. // 停止条件
  43. void I2C_Stop_A(void)
  44. {
  45.         AT24C32_SDA_LOW;
  46.         nops();

  47.         AT24C32_SCL_HIGH;
  48.         nops();

  49.         AT24C32_SDA_HIGH;
  50.         nops();

  51.         AT24C32_SCL_LOW;
  52.         nops();
  53. }

  54. // 产生一个时钟信号
  55. uint8_t I2C_GenerateClock(void)
  56. {
  57.         uint8_t bData = 0;
  58.         uint16_t t = 0;
  59.         
  60.         AT24C32_SCL_HIGH;        
  61.         nops();
  62.         for (t = 0; t < 6; t++);
  63.         
  64.         if (AT24C32_READSDA)
  65.         {
  66.                 bData = 1;
  67.         }
  68.         
  69.         AT24C32_SCL_LOW;               
  70.         nops();

  71.         return bData;
  72. }


  73. // 应答位
  74. void I2C_Ack(uint8_t bAckYes)
  75. {
  76.         if (bAckYes)
  77.         {
  78.                 AT24C32_SDA_LOW;
  79.         }
  80.         else
  81.         {
  82.                 AT24C32_SDA_HIGH;
  83.         }
  84.         
  85.         I2C_GenerateClock();
  86.         
  87.         AT24C32_SDA_HIGH;
  88. }

  89. // 发送数据子程序, ucData为要求发送的数据
  90. uint8_t I2C_Send(uint8_t ucData)
  91. {
  92.         uint8_t i;
  93.         
  94.         for (i = 0; i < 8; i++)
  95.         {
  96.                 if (ucData & 0x80)
  97.                 {
  98.                         AT24C32_SDA_HIGH;
  99.                 }
  100.                 else
  101.                 {
  102.                         AT24C32_SDA_LOW;
  103.                 }
  104.                
  105.                 ucData <<= 1;
  106.                
  107.                 I2C_GenerateClock();
  108.         }
  109.         
  110.         AT24C32_SDA_HIGH;
  111.         
  112.         return (!I2C_GenerateClock());
  113. }

  114. // 读一个字节的数据,并返回该字节值
  115. uint8_t I2C_Read(void)
  116. {
  117.         uint8_t ucData = 0;
  118.         uint8_t i;
  119.         
  120.         for (i = 0; i < 8; i++)
  121.         {
  122.                 ucData <<= 1;
  123.                
  124.                 if (I2C_GenerateClock())
  125.                 {
  126.                         ucData |= 1;
  127.                 }
  128.         }
  129.         return ucData;
  130. }

  131. // 设置下一步操作的地址(一字节地址)
  132. uint8_t I2C_SetAddress(uint16_t u16Addr)
  133. {
  134.         // 发送启动信号
  135.         I2C_Start_A();

  136.         // 发送访问地址
  137.         return (I2C_Send(I2C_SLAW) && I2C_Send(u16Addr));
  138. }

  139. // 设置下一步操作的地址(两字节地址)
  140. uint8_t I2C_SetAddress16(uint16_t u16Addr)
  141. {
  142.         // 发送启动信号
  143.         I2C_Start_A();
  144.         // 发送从器件地址

  145.         // 发送访问地址        
  146.         return (I2C_Send(I2C_SLAW) && I2C_Send(u16Addr >> 8) && I2C_Send(u16Addr & 0xff));
  147. }

  148. // 向8位地址的器件( 如:24C02 )写数据  bAddressIs16bit = 0
  149. // 向16位地址的器件( 如:24C32 )写数据 bAddressIs16bit = 1
  150. void Write_Byte_24c32(uint8_t *pDat, uint16_t u16Addr, uint8_t ucNbyte, uint8_t bAddressIs16bit)
  151. {
  152.         uint8_t bWriteError = FALSE;        //写数据出错标志位
  153.         uint8_t i;
  154.         uint8_t ucDataBuf = 0;
  155.         uint32_t Cnt = 0;

  156.         for (i = 0; i < ucNbyte; i++)
  157.         {
  158.                 if (bAddressIs16bit)
  159.                 {
  160.                         if (I2C_SetAddress16(u16Addr + i) == 0)
  161.                         {
  162.                                 bWriteError = TRUE;
  163.                                 break;
  164.                         }
  165.                 }
  166.                 else
  167.                 {
  168.                         if (I2C_SetAddress(u16Addr + i) == 0)
  169.                         {
  170.                                 bWriteError = TRUE;
  171.                                 break;
  172.                         }
  173.                 }

  174.                 ucDataBuf = *(pDat + i);

  175.                 if (I2C_Send(ucDataBuf) == 0)
  176.                 {
  177.                         bWriteError = TRUE;
  178.                         break;
  179.                 }
  180.                
  181.                 I2C_Stop_A();
  182.                
  183.                 ucDataBuf = 12;
  184.                
  185.                 do
  186.                 {
  187.                         I2C_Start_A();
  188.                 } while ((++Cnt < 500000) && I2C_Send(I2C_SLAW) == 0);
  189.                
  190.                 //如果50mS内还没有响应,则进入保护性报警状态
  191.                 if (Cnt == 500000)
  192.                 {
  193.                         bWriteError = TRUE;
  194.                         break;
  195.                 }
  196.                
  197.                 I2C_Stop_A();
  198.         }
  199.         
  200.         if (bWriteError)
  201.         {
  202.                 I2C_Error();
  203.         }
  204. }

  205. // 从8位地址的器件( 如:24C02 )读数据  bAddressIs16bit = 0
  206. // 从16位地址的器件( 如:24C32 )读数据 bAddressIs16bit = 1
  207. void Read_nByte_24c32(uint8_t *pDat, uint16_t u16Addr, uint8_t ucNbyte, uint8_t bAddressIs16bit)
  208. {
  209.         uint8_t bReadError标志寄存器 = FALSE;        //读EEPROM错误标志位
  210.         uint8_t i;
  211.         uint8_t ucDataBuf = 0;

  212.         if (bAddressIs16bit)
  213.         {
  214.                 if (I2C_SetAddress16(u16Addr) == 0)
  215.                 {
  216.                         bReadError = TRUE;
  217.                 }
  218.         }
  219.         else
  220.         {
  221.                 if (I2C_SetAddress(u16Addr) == 0)
  222.                 {
  223.                         bReadError = TRUE;
  224.                 }
  225.         }

  226.         if (bReadError)
  227.         {
  228.                 I2C_Error();
  229.         }

  230.         I2C_Start_A();

  231.         if (I2C_Send(I2C_SLAR) == 0)
  232.         {
  233.                 bReadError = TRUE;
  234.         }

  235.         if (bReadError)
  236.         {
  237.                 I2C_Error();
  238.         }

  239.         for (i = 0; i < ucNbyte; i++)
  240.         {
  241.                 ucDataBuf = I2C_Read();

  242.                 *(pDat + i) = ucDataBuf;
  243.                 I2C_Ack(i != (ucNbyte - 1));
  244.         }
  245.         
  246.         I2C_Stop_A();
  247. }

  248. uint8_t Read_Byte_24c32(uint16_t u16Addr)
  249. {
  250.         uint8_t EepromDataBuf = 0xFF;
  251.         //从16位地址的器件( 如:24C32 )读数据 bAddressIs16bit = 1

  252.         Read_nByte_24c32(&EepromDataBuf, u16Addr, 1, DEVICE_ADDR);
  253.                
  254.         return EepromDataBuf;
  255. }

  256. //修改EEPROM数据
  257. void ModifData_24c32(uint16_t u16Addr, uint8_t *pDat, uint16_t uclen)
  258. {
  259.         //向16位地址的器件( 如:24C32 )写数据 bAddressIs16bit = 1
  260.         Write_Byte_24c32(pDat, u16Addr, uclen, DEVICE_ADDR);
  261. }

STM8S005K6_24Cxx_IIC_Test_Simulator.zip (301.58 KB, 下载次数: 623)


zh113214 发表于 2015-1-25 17:07 | 显示全部楼层
为啥对于单片机接SDA脚的IO口进行方向控制反而引起时序不正常而读写不了数据,这个我也看的不是很懂
VDeveloper 发表于 2015-5-19 17:27 | 显示全部楼层
测试通过,谢谢楼主……
309030106 发表于 2015-5-19 20:26 | 显示全部楼层
感谢分享
zdaly 发表于 2015-5-20 09:54 | 显示全部楼层
用软件模拟是因为什么呢?STM8自己的硬件I2C不能用吗?
paier_tt 发表于 2015-5-20 11:42 | 显示全部楼层
完全看不懂,不过还是支持你一下
 楼主| owenli520 发表于 2015-5-21 08:39 | 显示全部楼层
zdaly 发表于 2015-5-20 09:54
用软件模拟是因为什么呢?STM8自己的硬件I2C不能用吗?

硬件I2C能用就不用模拟了,折腾很久硬件I2C就是不行
wowow 发表于 2015-5-21 08:55 | 显示全部楼层
STM32下是设成开集电极输出的,STM8没试过,应该也可以。
周董 发表于 2015-5-21 09:38 | 显示全部楼层
楼主怎么解决的啊,可以分享下吗
zdaly 发表于 2015-5-28 09:47 | 显示全部楼层
owenli520 发表于 2015-5-21 08:39
硬件I2C能用就不用模拟了,折腾很久硬件I2C就是不行

硬件I2C还是可以用的,目前我遇到的问题只是不能连续读,其他的都还好。
linlulu 发表于 2015-12-8 12:00 | 显示全部楼层
正在调试STM051的,用得着!多谢!!
tianyu22 发表于 2016-5-19 13:39 | 显示全部楼层
遇到个问题。。stm8s005无法调试,借你代码一用
shizaigaole 发表于 2016-5-20 10:27 | 显示全部楼层
有时间的话,
还是建议把硬件IIC模块用起来。

否则每次有点困难,就退缩回去,
不是好习惯。
大好大 发表于 2016-8-22 16:06 | 显示全部楼层
为什么下载来附件不完整打不开啊楼主
Glory分享 发表于 2016-11-2 16:23 | 显示全部楼层
楼主 我下载的为啥打不开呢?
干掉9527 发表于 2017-3-29 21:43 | 显示全部楼层
谢谢分享,刚好试一下
feelhyq 发表于 2017-3-30 09:18 | 显示全部楼层
针对楼主的疑问,将IO方向改变出现异常。  在改变IO方向之前,将SDA线释放掉,即将SDA拉高,原因是 SDA是开漏的,一旦被拉低之后,SDA就直接接地了,从器件是无法将SDA拉高的,也就无法传输数据。
simonliu009 发表于 2017-5-7 11:47 | 显示全部楼层
谢谢,早就听说STM8的I2C有bug,有个模拟的就好多了
10299823 发表于 2017-5-7 17:22 | 显示全部楼层
模拟iic好调试。
10299823 发表于 2017-5-7 17:28 | 显示全部楼层
iic读取EEPROM的代码很多。
您需要登录后才可以回帖 登录 | 注册

本版积分规则

7

主题

57

帖子

0

粉丝
快速回复 在线客服 返回列表 返回顶部