菜农NUC100学习笔记4(从无败绩的I2C主控模式)

[复制链接]
 楼主| hotpower 发表于 2010-12-9 23:19 | 显示全部楼层 |阅读模式
俺这个I2C套路至少8年无败绩,它是个通用的I2C主机控制模式,对象可以是任何I2C从机设备。

它在AVR,PIC,DSP,ARM上全是同一套路,可以搜索"I2C TWI SMBus HotPower"
http://www.baigoogledu.com/s.php?q=I2C+TWI+SMBus+HotPower&num=10

先将在华邦的CM0上的实现作为笔记全文发布,它已经过测试证明是无错误的。

转载请注明“雁塔菜地”

1.I2C.H

  1. #include "main.h"
  2. #ifndef __NUC1xxI2c_H__
  3. #define __NUC1xxI2c_H__
  4. #ifdef __cplusplus
  5. extern "C" {
  6. #endif
  7. class I2cObj;
  8. class I2cObj {//I2C类
  9. public://构造函数
  10.     I2cObj(unsigned int I2CAddress = 0xa0);//默认通用EEPROM地址
  11. private://私有方法
  12.     inline void I2cInit(void);//I2C接口初始化
  13.     inline void Open(void);//打开I2C接口
  14.     inline void Start(void);//复位I2C
  15.     inline void REStart(void);//重复位I2C
  16. inline void Wait(void);//超时等待
  17. inline void Exit(void);//出错退出
  18.     inline void Stop(void);//成功结束
  19. public://公有方法及属性
  20. static void Exec(void);//I2C中断回调函数(方法)
  21. unsigned char ReadByte(unsigned int, unsigned char &);//读字节函数(方法)
  22. unsigned char WriteByte(unsigned int, unsigned char);//写字节函数(方法)
  23. unsigned char ReadBuffer(unsigned int, unsigned char *, unsigned int);//读块函数(方法)
  24. unsigned char WriteBuffer(unsigned int, unsigned char *, unsigned int);//写块函数(方法)
  25. void SetAddress(unsigned int I2CAddress);//公有只写方法(属性)
  26. void WriteWait(void);//EEPROM写入等待函数(方法)
  27. unsigned int GetState(void);//公有只读方法(属性)
  28. void SetSystickCount(void);//公有只写方法(属性)
  29. private://私有属性
  30. volatile bool Busy;//忙属性
  31. volatile unsigned int State;//私有属性
  32. volatile unsigned int SystickCount;//私有属性
  33. volatile unsigned int Count;//私有属性
  34. volatile unsigned int MainCount, SubCount;//主从计数
  35. volatile unsigned int SubAddr;//私有属性
  36. volatile unsigned char MainComm, SubComm;//主从通讯命令
  37. volatile unsigned char TxBuffer[16], RxBuffer[16];//接收数据缓冲区
  38. public://公有属性
  39. };
  40. #ifdef __cplusplus
  41. }
  42. #endif
  43. #endif//__NUC1xxI2c_H__



2.I2C.CPP

  1. #include "i2c.h"
  2. I2cObj::I2cObj (unsigned int I2CAddress)
  3. {
  4. SubAddr = I2CAddress;
  5.     I2cInit();
  6. }
  7. void I2cObj::I2cInit(void)
  8. {
  9. Open();
  10. for (int i = 0; i < sizeof(TxBuffer); i ++) {
  11.   TxBuffer[i] = 0;
  12.   RxBuffer[i] = 0;
  13. }
  14. Exit();//通讯失败
  15. }
  16. void I2cObj::Open(void)
  17. {
  18.    SYSs.GPAMFP.Regs |= (1 << GCR_GPAMFP_I2C0_SDA)//PA.8 Pin功能选择I2C0_SDA
  19.                   |  (1 << GCR_GPAMFP_I2C0_SCL);//PA.9 Pin功能选择I2C0_SCL
  20. SYSCLKs.APBCLK.Bits.I2C0_EN = 1;
  21. SYSs.IPRSTC2.Bits.I2C0_RST = 1;
  22. SYSs.IPRSTC2.Bits.I2C0_RST = 0;
  23. I2C0s.CLK.Regs = ((DrvSYS_GetHCLKFreq()*10)/(50000 * 4) + 5) / 10 - 1;
  24. I2C0s.CON.Regs &= ~((1 << I2C_CON_STA)
  25.                  |   (1 << I2C_CON_STO)
  26.                  |   (1 << I2C_CON_AA));
  27. I2C0s.CON.Regs |= (1 << I2C_CON_EI)
  28.                  | (1 << I2C_CON_ENSI)
  29.         | (1 << I2C_CON_SI);
  30. comIRQ_InstallCallbackFn(I2C0_IRQn, &I2cObj::Exec);//安装I2C回调函数,并打开I2C中断
  31. // comIRQ_InstallCallbackFn(I2C0_IRQn, &I2c.Exec);//安装I2C回调函数,并打开I2C中断
  32. }
  33. unsigned int I2cObj::GetState(void)
  34. {
  35. return State;
  36. }
  37. void I2cObj::SetSystickCount(void)
  38. {
  39. SystickCount ++;
  40. }
  41. void I2cObj::SetAddress(unsigned int I2CAddress)
  42. {
  43. SubAddr = I2CAddress;
  44. }
  45. void I2cObj::Start(void)
  46. {
  47. Busy = true;
  48.    State = I2C_START;//主机准备发送启始位
  49.    Count = 0;//发送数据个数
  50. SystickCount = 0;//清除节拍计数器
  51. I2C0s.CON.Regs &= ~((1 << I2C_CON_STO)
  52.                  |   (1 << I2C_CON_AA));
  53. I2C0s.CON.Regs |= (1 << I2C_CON_EI)
  54.                  | (1 << I2C_CON_ENSI)
  55.         | (1 << I2C_CON_SI);
  56. I2C0s.CON.Bits.STA = 1;
  57. Wait();
  58. }
  59. void I2cObj::REStart(void)
  60. {
  61. Busy = true;
  62.    State = I2C_REP_START;//主机准备发送启始位
  63.    Count = 0;//发送数据个数
  64. I2C0s.CON.Regs |=  (1 << I2C_CON_STA) | (1 << I2C_CON_SI);
  65. }
  66. void I2cObj::Stop(void)
  67. {
  68. Busy = false;
  69. State = I2C_BUS_OK;//通讯成功
  70. I2C0s.CON.Regs |= (1 << I2C_CON_STO) | (1 << I2C_CON_SI);
  71. }
  72. void I2cObj::Exit(void)
  73. {
  74. Busy = false;
  75. State = I2C_BUS_ERROR;//通讯失败
  76.   I2C0s.CON.Bits.SI = 1;//清除中断标志
  77. }
  78. void I2cObj::Wait(void)
  79. {
  80. SystickCount = 0;//清除节拍计数器
  81. while (Busy && SystickCount < 10);//100mS超时
  82. }
  83. void I2cObj::WriteWait(void)
  84. {
  85. SystickCount = 0;//清除节拍计数器
  86. while (SystickCount < 10);//延时100mS等待写入完成
  87. }
  88. //I2C回调函数(静态成员函数)
  89. void I2cObj::Exec(void)
  90. {
  91.   switch(I2C0s.STATUS.Regs & 0xf8) {
  92.     case I2C_START://主机收到自己发送的开始信号
  93.         if (I2c.State == I2C_START) {//本次中断应该接收TW_START信号
  94.           I2C0s.DATA.Regs = I2c.SubAddr & 0xfe;//发送子机地址(写)
  95.           I2C0s.CON.Bits.STA = 0;//此位必须清除,否则死机
  96.      I2C0s.CON.Bits.SI = 1;//清除中断标志
  97.     I2c.State = I2C_MT_SLA_ACK;//Status下次I2C_MT_SLA_ACK
  98.      }
  99.      else I2c.Exit();//通讯失败
  100.         break;
  101. case I2C_REP_START://主机收到自己发送的重新开始信号
  102.         if (I2c.State == I2C_REP_START) {//本次中断应该接收TW_RESTART信号
  103.           I2C0s.DATA.Regs = I2c.SubAddr | 0x01;//发送子机地址(读)
  104.        I2c.State = I2C_MR_SLA_ACK;//Status下次I2C_MR_SLA_ACK
  105.     I2C0s.CON.Bits.STA = 0;//此位必须清除,否则读不出数据
  106.     I2C0s.CON.Regs |= (1 << I2C_CON_SI) | (1 << I2C_CON_AA);
  107.      }
  108.      else I2c.Exit();//通讯失败
  109.         break;
  110. case I2C_MT_SLA_ACK://主发机接收到从机的地址应答信号后发送命令
  111.         if (I2c.State == I2C_MT_SLA_ACK) {//本次中断应该接收TW_MT_SLA_ACK信号
  112.        I2c.State = I2C_MT_DATA_ACK;//Status下次应该收TW_MT_DATA_ACK
  113.              I2C0s.DATA.Regs = I2c.SubComm;//发送子机命令
  114.     I2C0s.CON.Regs |= (1 << I2C_CON_SI) | (1 << I2C_CON_AA);
  115.      }
  116.      else I2c.Exit();//通讯失败
  117.         break;
  118. case I2C_MR_SLA_ACK://主收机接收到从机的地址应答信号
  119.         if ((I2c.State == I2C_MR_SLA_ACK) && I2c.SubCount) {//本次中断应该接收TW_MR_SLA_ACK信号
  120.        I2c.State = I2C_MR_DATA_ACK;//Status下次应该收TW_MR_DATA_ACK
  121. //    I2C0s.DATA.Regs = 0XFF;//
  122.     I2C0s.CON.Bits.SI = 1;//清除中断标志
  123.      }
  124.      else I2c.Exit();//通讯失败
  125.      break;
  126. case I2C_MT_DATA_ACK://主收机接收到从机的数据应答信号
  127.         if ((I2c.State == I2C_MT_DATA_ACK) && (I2c.Count < I2c.MainCount)) {//本次中断应该接收TW_MT_DATA_ACK信号
  128.           I2C0s.DATA.Regs = I2c.TxBuffer[I2c.Count ++];//发送子机数据
  129.        I2C0s.CON.Regs |= (1 << I2C_CON_SI) | (1 << I2C_CON_AA);
  130.      }
  131.      else {
  132.           if ((I2c.State == I2C_MT_DATA_ACK) && (I2c.Count == I2c.MainCount) && (I2c.SubAddr & 1)) {//本次中断应该接收TW_MT_DATA_ACK信号
  133.            I2c.REStart();//
  134.     }
  135.              else I2c.Stop();//通讯成功
  136.      }
  137.      break;
  138. case I2C_MR_DATA_ACK:
  139.         if ((I2c.State == I2C_MR_DATA_ACK) && (I2c.Count < I2c.SubCount)) {
  140.           I2c.RxBuffer[I2c.Count ++] = I2C0s.DATA.Regs;//接收子机数据
  141.     if (I2c.Count < I2c.SubCount) {
  142.          I2C0s.CON.Regs |= (1 << I2C_CON_SI) | (1 << I2C_CON_AA);//主机转入接收状态
  143.     }
  144.     else {
  145.       I2C0s.CON.Bits.AA = 0;//清除ACK标志
  146.       I2C0s.CON.Bits.SI = 1;//清除中断标志
  147.       I2c.State = I2C_MR_DATA_NACK;//下次进入I2C_MR_DATA_NACK,接收数据准备完成
  148.     }
  149.      }
  150.      else I2c.Exit();//通讯失败
  151.      break;
  152. case I2C_MR_DATA_NACK://数据接收结束
  153.         if ((I2c.State == I2C_MR_DATA_NACK) && (I2c.Count == I2c.SubCount)) {
  154.        I2c.Stop();//通讯成功
  155.      }
  156.      else I2c.Exit();//通讯失败
  157.      break;
  158. // case I2C_MT_DATA_NACK:
  159. //     Exit();//通讯失败
  160. //     break;
  161. default:
  162.      I2c.Exit();//通讯失败
  163.   }
  164. }
  165. unsigned char I2cObj::ReadByte(unsigned int Address, unsigned char &Data)
  166. {
  167. SubAddr |= 0x01;
  168. MainCount = 0;//发送0个数据(只读)
  169. //本程序为通用I2C,故发送器件后一般为命令,对EEPROM来说,命令实际是EEPROM地址
  170. SubComm = Address;//读出地址
  171. SubCount = 1;//接收1个数据
  172. Start();//启动I2C模块
  173. if (State == I2C_BUS_OK) {//通讯成功
  174.   Data = RxBuffer[0];//从接收缓冲区取出一个字节
  175. }
  176. return State;//(读出数据在RxBuffer[0]~RxBuffer[15])
  177. }
  178. unsigned char I2cObj::ReadBuffer(unsigned int Address, unsigned char *Data, unsigned int Cnt)
  179. {
  180. SubAddr |= 0x01;
  181. MainCount = 0;//发送0个数据(只读)
  182. //本程序为通用I2C,故发送器件后一般为命令,对EEPROM来说,命令实际是EEPROM地址
  183. SubComm = Address;//读出地址
  184. SubCount = (Cnt <= sizeof(RxBuffer)) ? Cnt : sizeof(RxBuffer);//接收Cnt个数据
  185. Start();//启动I2C模块
  186. if (State == I2C_BUS_OK) {//通讯成功
  187.   for (int i = 0; i < SubCount; i ++) Data[i] = RxBuffer[i];//从接收缓冲区取出Cnt个字节
  188. }
  189. return State;//(读出数据在RxBuffer[0]~RxBuffer[15])
  190. }
  191. unsigned char I2cObj::WriteByte(unsigned int Address, unsigned char Data)
  192. {
  193. SubAddr &= 0xfe;
  194. MainCount = 1;//发送1个数据
  195. //本程序为通用I2C,故发送器件后一般为命令,对EEPROM来说,命令实际是EEPROM地址
  196. SubComm = Address;//写入地址
  197. TxBuffer[0] = Data;//写入1个数据到发送缓冲区
  198. SubCount = 0;//接收0个数据
  199. Start();//启动I2C模块
  200. WriteWait();//延时100mS等待写入完成
  201. return State;
  202. }
  203. unsigned char I2cObj::WriteBuffer(unsigned int Address, unsigned char *Data, unsigned int Cnt)
  204. {
  205. SubAddr &= 0xfe;
  206. MainCount = (Cnt <= sizeof(TxBuffer)) ? Cnt : sizeof(TxBuffer);//发送Cnt个数据
  207. //本程序为通用I2C,故发送器件后一般为命令,对EEPROM来说,命令实际是EEPROM地址
  208. SubComm = Address;//写入地址
  209. for (int i = 0; i < MainCount; i ++) TxBuffer[i] = Data[i];//写入Cnt个数据到发送缓冲区
  210. SubCount = 0;//接收0个数据
  211. Start();//启动I2C模块
  212. WriteWait();//延时100mS等待写入完成
  213. return State;
  214. }
  215. /*

  216. */

新好男孩 发表于 2010-12-10 08:18 | 显示全部楼层
呵呵,C++,现在正在学C++的类与对象,很好的范例,收藏了。
pkat 发表于 2010-12-10 18:59 | 显示全部楼层
这个很强大啊,居然从无败绩
cjd88ei75 发表于 2010-12-10 23:39 | 显示全部楼层
谢谢
cjd88ei75 发表于 2010-12-11 10:45 | 显示全部楼层
这个很强大啊
思行合一 发表于 2010-12-11 11:35 | 显示全部楼层
收藏了,谢谢
dbayj 发表于 2011-3-25 11:29 | 显示全部楼层
学习
无冕之王 发表于 2011-3-25 16:19 | 显示全部楼层
相当不错
 楼主| hotpower 发表于 2011-3-25 17:06 | 显示全部楼层
的确无一次败迹…
murex 发表于 2011-3-25 19:52 | 显示全部楼层
相当不错,俺保存了,以后俺也可以用了
 楼主| hotpower 发表于 2011-3-26 21:36 | 显示全部楼层
俺所有的i2c全部是一个套路,非常好用。
绝不夸张,是俺见过的最稳定及通用的。
俺要细说大家就可以任意应用…
tear086 发表于 2011-3-27 11:56 | 显示全部楼层
:victory:
Jarvis 发表于 2011-3-27 12:26 | 显示全部楼层
大叔请把main.h 也上传吧~~~
 楼主| hotpower 发表于 2011-3-28 11:31 | 显示全部楼层
群里有工程包提供下载,这里太小,必须分包上传
ichuangj 发表于 2011-7-28 16:42 | 显示全部楼层
好牛啊!顶:D
 楼主| hotpower 发表于 2011-7-28 17:44 | 显示全部楼层
这个模式真没失败过,没失败过的感觉。
hqgboy 发表于 2011-7-29 11:46 | 显示全部楼层
以前用了一个IO模拟的,很好用。
是网友的。好像叫甲方乙方。
 楼主| hotpower 发表于 2011-7-29 12:00 | 显示全部楼层
有硬件iic,模拟没必要。
cecwxf 发表于 2011-8-3 13:16 | 显示全部楼层
:victory:
哲哲55 发表于 2012-4-10 09:58 | 显示全部楼层
C++,表示不会。。。
只会C
您需要登录后才可以回帖 登录 | 注册

本版积分规则

1460

主题

21619

帖子

508

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