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

2010-12-9 23:23 2807 9 9 分类: MCU/ 嵌入式
俺这个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 = 0;

  12.   RxBuffer = 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 = RxBuffer;//从接收缓冲区取出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 = Data;//写入Cnt个数据到发送缓冲区

  210. SubCount = 0;//接收0个数据

  211. Start();//启动I2C模块

  212. WriteWait();//延时100mS等待写入完成

  213. return State;

  214. }

  215. /*



  216. */
复制代码

文章评论0条评论)

登录后参与讨论
我要评论
0
9
关闭 站长推荐上一条 /2 下一条