打印

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

[复制链接]
6581|21
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
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
 
#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[i] = 0;
  RxBuffer[i] = 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[i] = RxBuffer[i];//从接收缓冲区取出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[i] = Data[i];//写入Cnt个数据到发送缓冲区
SubCount = 0;//接收0个数据
Start();//启动I2C模块
WriteWait();//延时100mS等待写入完成
return State;
}
/*

*/

相关帖子

沙发
新好男孩| | 2010-12-10 08:18 | 只看该作者
呵呵,C++,现在正在学C++的类与对象,很好的范例,收藏了。

使用特权

评论回复
板凳
pkat| | 2010-12-10 18:59 | 只看该作者
这个很强大啊,居然从无败绩

使用特权

评论回复
地板
cjd88ei75| | 2010-12-10 23:39 | 只看该作者
谢谢

使用特权

评论回复
5
cjd88ei75| | 2010-12-11 10:45 | 只看该作者
这个很强大啊

使用特权

评论回复
6
思行合一| | 2010-12-11 11:35 | 只看该作者
收藏了,谢谢

使用特权

评论回复
7
dbayj| | 2011-3-25 11:29 | 只看该作者
学习

使用特权

评论回复
8
无冕之王| | 2011-3-25 16:19 | 只看该作者
相当不错

使用特权

评论回复
9
hotpower|  楼主 | 2011-3-25 17:06 | 只看该作者
的确无一次败迹…

使用特权

评论回复
10
murex| | 2011-3-25 19:52 | 只看该作者
相当不错,俺保存了,以后俺也可以用了

使用特权

评论回复
11
hotpower|  楼主 | 2011-3-26 21:36 | 只看该作者
俺所有的i2c全部是一个套路,非常好用。
绝不夸张,是俺见过的最稳定及通用的。
俺要细说大家就可以任意应用…

使用特权

评论回复
12
tear086| | 2011-3-27 11:56 | 只看该作者
:victory:

使用特权

评论回复
13
Jarvis| | 2011-3-27 12:26 | 只看该作者
大叔请把main.h 也上传吧~~~

使用特权

评论回复
14
hotpower|  楼主 | 2011-3-28 11:31 | 只看该作者
群里有工程包提供下载,这里太小,必须分包上传

使用特权

评论回复
15
ichuangj| | 2011-7-28 16:42 | 只看该作者
好牛啊!顶:D

使用特权

评论回复
16
hotpower|  楼主 | 2011-7-28 17:44 | 只看该作者
这个模式真没失败过,没失败过的感觉。

使用特权

评论回复
17
hqgboy| | 2011-7-29 11:46 | 只看该作者
以前用了一个IO模拟的,很好用。
是网友的。好像叫甲方乙方。

使用特权

评论回复
18
hotpower|  楼主 | 2011-7-29 12:00 | 只看该作者
有硬件iic,模拟没必要。

使用特权

评论回复
19
cecwxf| | 2011-8-3 13:16 | 只看该作者
:victory:

使用特权

评论回复
20
哲哲55| | 2012-4-10 09:58 | 只看该作者
C++,表示不会。。。
只会C

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

1460

主题

21619

帖子

506

粉丝