原创
菜农NUC100学习笔记4(从无败绩的I2C主控模式)
俺这个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
- #include "main.h"
- #ifndef __NUC1xxI2c_H__
- #define __NUC1xxI2c_H__
- #ifdef __cplusplus
- extern "C" {
- #endif
- class I2cObj;
- class I2cObj {//I2C类
- public://构造函数
- I2cObj(unsigned int I2CAddress = 0xa0);//默认通用EEPROM地址
- private://私有方法
- inline void I2cInit(void);//I2C接口初始化
- inline void Open(void);//打开I2C接口
- inline void Start(void);//复位I2C
- inline void REStart(void);//重复位I2C
- inline void Wait(void);//超时等待
- inline void Exit(void);//出错退出
- inline void Stop(void);//成功结束
- public://公有方法及属性
- static void Exec(void);//I2C中断回调函数(方法)
- unsigned char ReadByte(unsigned int, unsigned char &);//读字节函数(方法)
- unsigned char WriteByte(unsigned int, unsigned char);//写字节函数(方法)
- unsigned char ReadBuffer(unsigned int, unsigned char *, unsigned int);//读块函数(方法)
- unsigned char WriteBuffer(unsigned int, unsigned char *, unsigned int);//写块函数(方法)
- void SetAddress(unsigned int I2CAddress);//公有只写方法(属性)
- void WriteWait(void);//EEPROM写入等待函数(方法)
- unsigned int GetState(void);//公有只读方法(属性)
- void SetSystickCount(void);//公有只写方法(属性)
- private://私有属性
- volatile bool Busy;//忙属性
- volatile unsigned int State;//私有属性
- volatile unsigned int SystickCount;//私有属性
- volatile unsigned int Count;//私有属性
- volatile unsigned int MainCount, SubCount;//主从计数
- volatile unsigned int SubAddr;//私有属性
- volatile unsigned char MainComm, SubComm;//主从通讯命令
- volatile unsigned char TxBuffer[16], RxBuffer[16];//接收数据缓冲区
- public://公有属性
- };
- #ifdef __cplusplus
- }
- #endif
- #endif//__NUC1xxI2c_H__
复制代码2.I2C.CPP
- #include "i2c.h"
- I2cObj::I2cObj (unsigned int I2CAddress)
- {
- SubAddr = I2CAddress;
- I2cInit();
- }
- void I2cObj::I2cInit(void)
- {
- Open();
- for (int i = 0; i < sizeof(TxBuffer); i ++) {
- TxBuffer = 0;
- RxBuffer = 0;
- }
- Exit();//通讯失败
- }
- void I2cObj::Open(void)
- {
- SYSs.GPAMFP.Regs |= (1 << GCR_GPAMFP_I2C0_SDA)//PA.8 Pin功能选择I2C0_SDA
- | (1 << GCR_GPAMFP_I2C0_SCL);//PA.9 Pin功能选择I2C0_SCL
- SYSCLKs.APBCLK.Bits.I2C0_EN = 1;
- SYSs.IPRSTC2.Bits.I2C0_RST = 1;
- SYSs.IPRSTC2.Bits.I2C0_RST = 0;
- I2C0s.CLK.Regs = ((DrvSYS_GetHCLKFreq()*10)/(50000 * 4) + 5) / 10 - 1;
- I2C0s.CON.Regs &= ~((1 << I2C_CON_STA)
- | (1 << I2C_CON_STO)
- | (1 << I2C_CON_AA));
- I2C0s.CON.Regs |= (1 << I2C_CON_EI)
- | (1 << I2C_CON_ENSI)
- | (1 << I2C_CON_SI);
- comIRQ_InstallCallbackFn(I2C0_IRQn, &I2cObj::Exec);//安装I2C回调函数,并打开I2C中断
- // comIRQ_InstallCallbackFn(I2C0_IRQn, &I2c.Exec);//安装I2C回调函数,并打开I2C中断
- }
- unsigned int I2cObj::GetState(void)
- {
- return State;
- }
- void I2cObj::SetSystickCount(void)
- {
- SystickCount ++;
- }
- void I2cObj::SetAddress(unsigned int I2CAddress)
- {
- SubAddr = I2CAddress;
- }
- void I2cObj::Start(void)
- {
- Busy = true;
- State = I2C_START;//主机准备发送启始位
- Count = 0;//发送数据个数
- SystickCount = 0;//清除节拍计数器
- I2C0s.CON.Regs &= ~((1 << I2C_CON_STO)
- | (1 << I2C_CON_AA));
- I2C0s.CON.Regs |= (1 << I2C_CON_EI)
- | (1 << I2C_CON_ENSI)
- | (1 << I2C_CON_SI);
- I2C0s.CON.Bits.STA = 1;
- Wait();
- }
- void I2cObj::REStart(void)
- {
- Busy = true;
- State = I2C_REP_START;//主机准备发送启始位
- Count = 0;//发送数据个数
- I2C0s.CON.Regs |= (1 << I2C_CON_STA) | (1 << I2C_CON_SI);
- }
- void I2cObj::Stop(void)
- {
- Busy = false;
- State = I2C_BUS_OK;//通讯成功
- I2C0s.CON.Regs |= (1 << I2C_CON_STO) | (1 << I2C_CON_SI);
- }
- void I2cObj::Exit(void)
- {
- Busy = false;
- State = I2C_BUS_ERROR;//通讯失败
- I2C0s.CON.Bits.SI = 1;//清除中断标志
- }
- void I2cObj::Wait(void)
- {
- SystickCount = 0;//清除节拍计数器
- while (Busy && SystickCount < 10);//100mS超时
- }
- void I2cObj::WriteWait(void)
- {
- SystickCount = 0;//清除节拍计数器
- while (SystickCount < 10);//延时100mS等待写入完成
- }
- //I2C回调函数(静态成员函数)
- void I2cObj::Exec(void)
- {
- switch(I2C0s.STATUS.Regs & 0xf8) {
- case I2C_START://主机收到自己发送的开始信号
- if (I2c.State == I2C_START) {//本次中断应该接收TW_START信号
- I2C0s.DATA.Regs = I2c.SubAddr & 0xfe;//发送子机地址(写)
- I2C0s.CON.Bits.STA = 0;//此位必须清除,否则死机
- I2C0s.CON.Bits.SI = 1;//清除中断标志
- I2c.State = I2C_MT_SLA_ACK;//Status下次I2C_MT_SLA_ACK
- }
- else I2c.Exit();//通讯失败
- break;
- case I2C_REP_START://主机收到自己发送的重新开始信号
- if (I2c.State == I2C_REP_START) {//本次中断应该接收TW_RESTART信号
- I2C0s.DATA.Regs = I2c.SubAddr | 0x01;//发送子机地址(读)
- I2c.State = I2C_MR_SLA_ACK;//Status下次I2C_MR_SLA_ACK
- I2C0s.CON.Bits.STA = 0;//此位必须清除,否则读不出数据
- I2C0s.CON.Regs |= (1 << I2C_CON_SI) | (1 << I2C_CON_AA);
- }
- else I2c.Exit();//通讯失败
- break;
- case I2C_MT_SLA_ACK://主发机接收到从机的地址应答信号后发送命令
- if (I2c.State == I2C_MT_SLA_ACK) {//本次中断应该接收TW_MT_SLA_ACK信号
- I2c.State = I2C_MT_DATA_ACK;//Status下次应该收TW_MT_DATA_ACK
- I2C0s.DATA.Regs = I2c.SubComm;//发送子机命令
- I2C0s.CON.Regs |= (1 << I2C_CON_SI) | (1 << I2C_CON_AA);
- }
- else I2c.Exit();//通讯失败
- break;
- case I2C_MR_SLA_ACK://主收机接收到从机的地址应答信号
- if ((I2c.State == I2C_MR_SLA_ACK) && I2c.SubCount) {//本次中断应该接收TW_MR_SLA_ACK信号
- I2c.State = I2C_MR_DATA_ACK;//Status下次应该收TW_MR_DATA_ACK
- // I2C0s.DATA.Regs = 0XFF;//
- I2C0s.CON.Bits.SI = 1;//清除中断标志
- }
- else I2c.Exit();//通讯失败
- break;
- case I2C_MT_DATA_ACK://主收机接收到从机的数据应答信号
- if ((I2c.State == I2C_MT_DATA_ACK) && (I2c.Count < I2c.MainCount)) {//本次中断应该接收TW_MT_DATA_ACK信号
- I2C0s.DATA.Regs = I2c.TxBuffer[I2c.Count ++];//发送子机数据
- I2C0s.CON.Regs |= (1 << I2C_CON_SI) | (1 << I2C_CON_AA);
- }
- else {
- if ((I2c.State == I2C_MT_DATA_ACK) && (I2c.Count == I2c.MainCount) && (I2c.SubAddr & 1)) {//本次中断应该接收TW_MT_DATA_ACK信号
- I2c.REStart();//
- }
- else I2c.Stop();//通讯成功
- }
- break;
- case I2C_MR_DATA_ACK:
- if ((I2c.State == I2C_MR_DATA_ACK) && (I2c.Count < I2c.SubCount)) {
- I2c.RxBuffer[I2c.Count ++] = I2C0s.DATA.Regs;//接收子机数据
- if (I2c.Count < I2c.SubCount) {
- I2C0s.CON.Regs |= (1 << I2C_CON_SI) | (1 << I2C_CON_AA);//主机转入接收状态
- }
- else {
- I2C0s.CON.Bits.AA = 0;//清除ACK标志
- I2C0s.CON.Bits.SI = 1;//清除中断标志
- I2c.State = I2C_MR_DATA_NACK;//下次进入I2C_MR_DATA_NACK,接收数据准备完成
- }
- }
- else I2c.Exit();//通讯失败
- break;
- case I2C_MR_DATA_NACK://数据接收结束
- if ((I2c.State == I2C_MR_DATA_NACK) && (I2c.Count == I2c.SubCount)) {
- I2c.Stop();//通讯成功
- }
- else I2c.Exit();//通讯失败
- break;
- // case I2C_MT_DATA_NACK:
- // Exit();//通讯失败
- // break;
- default:
- I2c.Exit();//通讯失败
- }
- }
- unsigned char I2cObj::ReadByte(unsigned int Address, unsigned char &Data)
- {
- SubAddr |= 0x01;
- MainCount = 0;//发送0个数据(只读)
- //本程序为通用I2C,故发送器件后一般为命令,对EEPROM来说,命令实际是EEPROM地址
- SubComm = Address;//读出地址
- SubCount = 1;//接收1个数据
- Start();//启动I2C模块
- if (State == I2C_BUS_OK) {//通讯成功
- Data = RxBuffer[0];//从接收缓冲区取出一个字节
- }
- return State;//(读出数据在RxBuffer[0]~RxBuffer[15])
- }
- unsigned char I2cObj::ReadBuffer(unsigned int Address, unsigned char *Data, unsigned int Cnt)
- {
- SubAddr |= 0x01;
- MainCount = 0;//发送0个数据(只读)
- //本程序为通用I2C,故发送器件后一般为命令,对EEPROM来说,命令实际是EEPROM地址
- SubComm = Address;//读出地址
- SubCount = (Cnt <= sizeof(RxBuffer)) ? Cnt : sizeof(RxBuffer);//接收Cnt个数据
- Start();//启动I2C模块
- if (State == I2C_BUS_OK) {//通讯成功
- for (int i = 0; i < SubCount; i ++) Data = RxBuffer;//从接收缓冲区取出Cnt个字节
- }
- return State;//(读出数据在RxBuffer[0]~RxBuffer[15])
- }
- unsigned char I2cObj::WriteByte(unsigned int Address, unsigned char Data)
- {
- SubAddr &= 0xfe;
- MainCount = 1;//发送1个数据
- //本程序为通用I2C,故发送器件后一般为命令,对EEPROM来说,命令实际是EEPROM地址
- SubComm = Address;//写入地址
- TxBuffer[0] = Data;//写入1个数据到发送缓冲区
- SubCount = 0;//接收0个数据
- Start();//启动I2C模块
- WriteWait();//延时100mS等待写入完成
- return State;
- }
- unsigned char I2cObj::WriteBuffer(unsigned int Address, unsigned char *Data, unsigned int Cnt)
- {
- SubAddr &= 0xfe;
- MainCount = (Cnt <= sizeof(TxBuffer)) ? Cnt : sizeof(TxBuffer);//发送Cnt个数据
- //本程序为通用I2C,故发送器件后一般为命令,对EEPROM来说,命令实际是EEPROM地址
- SubComm = Address;//写入地址
- for (int i = 0; i < MainCount; i ++) TxBuffer = Data;//写入Cnt个数据到发送缓冲区
- SubCount = 0;//接收0个数据
- Start();//启动I2C模块
- WriteWait();//延时100mS等待写入完成
- return State;
- }
- /*
- */
复制代码
文章评论(0条评论)
登录后参与讨论