前阵子,网上看到有人提问CC2541的I2C是否支持AT24CXX系列的存储器。正好项目需要,所以买了两块AT24C256进行 测试了下。毫无疑问,当然是可以支持的。

首先,需要强调的是CC2541具有I2C外设,而CC2540不具备,这也是它们的区别之一。所以,CC2451可以完美驱动带I2C的设备。

如果CC2541的程序是跑OSAL架构的,那么可以可以将它提供的i2c的源码直接拿来用,而不需做任何修改。下面就是基于OSAL架构的来讲的。

支持i2c的代码可以在BLE协议栈安装目录下的BLE-CC254x-1.3.2\Components\hal\target\CC2541ST目录下找到。在这个目录下,找到hal_i2c.c、hal_i2c.h、hal_sensor.c和hal_sensor.h这4个文件,拷贝到你的工程目录下,然后天添加到工程中。其中hal_i2c.c与hal_i2c.h两个文件支持了CC2541的i2c的基本实现函数,hal_sensor.c与hal_sensor.h这两个文件则是为具体的i2c设备提供了读写接口。下面就来仔细讲讲。

1、hal_i2c.c与hal_i2c.h的代码实现

要想使用i2c,当然要对i2c协议有一定的了解。如下图说所示,可以总结出来i2c必须要的几部分是:起始信号、设备地址、读写选择位,应答信号,以及停止位。

在使用i2c之前,当然需要先配置下i2c。在hal_i2c.c下,有 HalI2CInit()函数,用来配置CC2541的i2c,代码如下:

static uint8 i2cAddr;

void HalI2CInit(uint8 address, i2cClock_t clockRate)
{
 i2cAddr = address << 1;

I2C_WRAPPER_DISABLE();
 I2CADDR = 0; // no multi master support at this time
 I2C_CLOCK_RATE(clockRate);
 I2C_ENABLE();
}

上面代码中看起来像函数的语句其实是宏定义,用来配置相关的寄存器,他们在hal_i2c.c的最开始就宏定义过,这里就不细讲,自己看代码。这里定义了一个i2cAddr变量,用于保存i2c设备的地址。I2C_WRAPPER_DISABLE()关闭写保护,这样的话就可以支持读写操作了,I2C_CLOCK_RATE()则设置i2c的时钟速率,最后I2C_ENABLE()使能i2c。这样的话,i2c就初始化完成了。

i2c的起始信号和停止信号以及读写数据都是通过操作寄存器操作的。hal_i2c.c中给出了他们的宏定义:

#define I2C_STRT() st ( \
 I2CCFG &= ~I2C_SI; \
 I2CCFG |= I2C_STA; \
 while ((I2CCFG & I2C_SI) == 0); \
 I2CCFG &= ~I2C_STA; \
)

// Must set STO before clearing SI.
#define I2C_STOP() st ( \
 I2CCFG |= I2C_STO; \
 I2CCFG &= ~I2C_SI; \
 while ((I2CCFG & I2C_STO) != 0); \
)

// Stop clock-stretching and then read when it arrives.
#define I2C_READ(_X_) st ( \
 I2CCFG &= ~I2C_SI; \
 while ((I2CCFG & I2C_SI) == 0); \
 (_X_) = I2CDATA; \
)

// First write new data and then stop clock-stretching.
#define I2C_WRITE(_X_) st ( \
 I2CDATA = (_X_); \
 I2CCFG &= ~I2C_SI; \
 while ((I2CCFG & I2C_SI) == 0); \
)

起始信号的产生需要操作I2CCFG寄存器下的STA位来产生起始信号;停止信号通过操作I2CCFG下的STO位来产生停止信号;而读写操作只要读取I2CDATA数据和向I2CDATA写入数据就可以实现了。

hal_i2c.c中还给出了发送起始信号与设备地址以及控制读写的函数:i2cMstStrt(),如下:

static uint8 i2cMstStrt(uint8 RD_WRn)
{
 I2C_STRT();

if (I2CSTAT == mstStarted) /* A start condition has been transmitted */
 {
 I2C_WRITE(i2cAddr | RD_WRn);
 }

return I2CSTAT;
}

它有一个参数:RD_WRn,用来决定数据的读写,RD_WRn=1表示从i2c总线读取数据,RD_WRn=0则从i2c总线上写数据。

为了从i2c总线上读取数据,hal_i2c.c中给出了HalI2CRead()函数,代码如下:

uint8 HalI2CRead(uint8 len, uint8 *pBuf)
{
 uint8 cnt = 0;

if (i2cMstStrt(I2C_MST_RD_BIT) != mstAddrAckR)
 {
 len = 0;
 }
 if (len > 1)
 {
 I2C_SET_ACK();
 }
 while (len > 0)
 {
 // slave devices require NACK to be sent after reading last byte
 if (len == 1)
 {
 I2C_SET_NACK();
 }

// read a byte from the I2C interface
 I2C_READ(*pBuf++);
 cnt++;
 len--;

if (I2CSTAT != mstDataAckR)
 {
 if (I2CSTAT != mstDataNackR)
 {
 // something went wrong, so don't count last byte
 cnt--;
 }
 break;
 }
 }
 I2C_STOP();

return cnt;
}

这个函数带两个参数:len和pbuf。len是要读取的数据长度,而pbuf则用于保存在读取到的数据。先要调用i2cMstStrt(I2C_MST_RD_BIT)函数,发送起始信号以及设备地址,控制读写位为读。然后在while(1)中,调用I2C_READ()读取指定数量的字节。最后再发送一个停止信号。

为了向i2c总线上写数据,hal_i2c.c给出了HalI2CWrite()函数,代码如下:

uint8 HalI2CWrite(uint8 len, uint8 *pBuf)
{
 if (i2cMstStrt(0) != mstAddrAckW)
 {
 len = 0;
 }
 for (uint8 cnt = 0; cnt < len; cnt++)
 {
 I2C_WRITE(*pBuf++);

if (I2CSTAT != mstDataAckW)
 {
 if (I2CSTAT == mstDataNackW)
 {
 len = cnt + 1;
 }
 else
 {
 len = cnt;
 }
 break;
 }
 }
 I2C_STOP();
 return len;
}

这个函数带两个参数:lne和pBuf。len是要写的数据长度,pBuf则是要写的数据指针。跟HalI2CRead()函数差不多,先调用i2cMstStrt(I2C_MST_RD_BIT)函数发送起始信号以设备地址,选择读写位为写操作。然后在while()中调用I2C_WRITE()写入指定长度的数据。最后在发送一位停止信号。

2、hal_sensor.c与hal_sensor.h文件

如果对于AT24CXX系列中的AT2401、AT2402、AT2404、AT2408、AT2416这几种存储器,hal_sensor.c和hal_sensor.h可以不做任何修改就可以完全支持。但是如果对于大于16K的AT24CXX些列存储器则需要对这两个文件进行修改。为什么会存在这样的差别呢?原因在于他们的片内地址的范围。对于1K位,2K位,4K位,8K位,16K位芯片采用一个8位长的字节地址码,对于32K位以上的采用2个8位长的字节地址码直接寻址,而4K位,8K位,16K位配合页面地址来寻址的。hal_sensor给出的代码用的就是支持8为地址的。所以对于16k以上的存储器需要将hal_sensor的代码修改成支持2个8位地址。下面就分成上面两种情况进行说明。

对于AT2401、AT2402、AT2404、AT2408、AT2416这几种存储器,hal_sensor的代码可以不做任何修改,但是可以将除了HalSensorReadReg()和HalSensorWriteReg()这两个函数之外的其他函数全部去掉,因为都是不相关的代码。HalSensorReadReg()与HalSensorWriteReg()分别用向AT24CXX存储器指定的地址上读写指定的数数据。代码如下:

bool HalSensorReadReg(uint8 addr, uint8 *pBuf, uint8 nBytes)
{
 uint8 i = 0;

/* Send address we're reading from */
 if (HalI2CWrite(1,&addr) == 1)
 {
 /* Now read data */
 i = HalI2CRead(nBytes,pBuf);
 }

return i == nBytes;
}

bool HalSensorWriteReg(uint8 addr, uint8 *pBuf, uint8 nBytes)
{
 uint8 i;
 uint8 *p = buffer;

/* Copy address and data to local buffer for burst write */
 *p++ = addr;
 for (i = 0; i < nBytes; i++)
 {
 *p++ = *pBuf++;
 }
 nBytes++;

/* Send address and data */
 i = HalI2CWrite(nBytes, buffer);
 if ( i!= nBytes)
 HAL_TOGGLE_LED2();

return (i == nBytes);
}

可以发现,这两个函数的参数中的addr的类型是uint8,对应的1个字节的地址。它们的读写过程如下:

对于AT2432、AT2464、AT24128、AT24256这几种存在器的片内地址需要2个8为字节的数据需要将上面给出的HalSensorReadReg()与HalSensorWriteReg()修改成支持16为地址,代码如下:

bool HalSensorReadReg(uint16 addr, uint8 *pBuf, uint8 nBytes)
{
 uint8 i = 0;
 uint8 addrH, addrL;
 
 addrL = addr & 0xff;
 addrH = addr >> 8;

/* Send address we're reading from */
 if (HalI2CWrite(1, &addrH) == 1)
 if (HalI2CWrite(1, &addrL) == 1)
 {
 /* Now read data */
 i = HalI2CRead(nBytes,pBuf);
 }

return i == nBytes;
}

bool HalSensorWriteReg(uint16 addr, uint8 *pBuf, uint8 nBytes)
{
 uint8 i;
 uint8 *p = buffer;
 uint8 addrL = 0, addrH = 0;
 
 addrL = (uint8)(addr & 0xff);
 addrH = (uint8)(addr >> 8);

/* Copy address and data to local buffer for burst write */
 *p++ = addrH;
 *p++ = addrL;
 for (i = 0; i < nBytes; i++)
 {
 *p++ = *pBuf++;
 }
 nBytes += 2;

/* Send address and data */
 i = HalI2CWrite(nBytes, buffer);
 if ( i!= nBytes)
 HAL_TOGGLE_LED2();

return (i == nBytes);
}

需要注意的是,上面的这两函数的参数addr的数据类型改成了uint16,所以需要在这两个函数的代码中将这16位的地址拆成高8为地址与低8为地址,分别定义addrH和addrL保存。在发送2个8位地址时,高8位地址在前,低8为地址在后。它们的读写过程如下:

建议再在hal_sensor.c中编写一个halSensorInit()函数,调用hal_i2c.c中的HalI2CInit(),配置CC2541的i2c,代码如下:

void halSensorInit(void)
{
 HalI2CInit(0x50, i2cClock_533KHZ); 
}

其中0x50指得是i2c设备的地址。对于AT24CXX系列对于AT24C01~AT24C16来说它们的设备地址如下:

对于AT24C32及以上的存储器来说,它的设备地址如下:

可以发现,不同AT24CXX的地址需要根据上面的bit1~bit3决定,bit4~bit7都固定为0b1010。bit1~bit3跟存储器的硬件接线相关,对于我买的AT24C256模块,A0与A1引脚全部接地,则它的地址为:0b1010000,即0x50。

3、AT24CXX的读写

在应用代码中,调用上面给出的halSensorInit()函数初始化CC2541的i2c外设,然后调用HalSensorWriteReg()指定地址写入数据,然后再调用HalSensorReadReg()函数将该地址数据的上的值读取出来,实例代码如下:

static uint8 buffer[32] = {0};

halSensorinit();//初始化CC2541的i2c

halWriteReg(0x0000, "abcdefghij", 10);//向AT24C256的0x0000地址处写入数据

halReadReg(0x0000, buffer, 10);//从AT24C256的0x0000地址处读取10个字节数据

HalLcdWriteString(buffer, HAL_LCD_LINE_4);//将读取到的数据显示在LCD上

转载自 http://ziye334.lofter.com/post/2435a3_2a2e1b4

CC2541对AT24CXX系列存储器的支持相关推荐

  1. 关于W25QXX系列以及AT24CXX系列容量大小

    一.W25QXX系列容量大小 在使用SPI对W25Q64进行存取数据进行驱动编写时,不知道W25Q64的容量是多少,偶尔会Byte和bit给混淆了 1Byte=8bit (1个字节为8位!!) 以下为 ...

  2. FLASH--W25QXX系列存储器

    本来是学习 SPI 总线相关的资源,主要是还需要处理一下关于SPI设备的问题. W25Q系列存储器是一种按照块,按照扇区去处理的芯片. 以W25Q64为例: 大小: 64兆位==8兆字节,其中分为 大 ...

  3. [VC] 检测AVX系列指令集的支持级别(AVX、AVX2、F16C、FMA、FMA4、XOP)

    从2011年的Sandy Bridge微架构处理器开始,现在支持AVX系列指令集的处理器越来越多了.本文探讨如何用VC编写检测AVX系列指令集的程序,并利用了先前的CPUIDFIELD方案. 一.AV ...

  4. HD声卡开启麦克风调控补丁 | HD系列声卡不支持立体声混音的解决办法

    1.Realtek HD Audio声卡开趔克风调控补丁 1)解决Realtek ALC 861/883/885声卡音量控制的录音部分无法调节音量问题 2)解决Realtek ALC 861/883/ ...

  5. 16系列显卡支持的计算机系统,老卡福音:NVIDIA GeForce GTX 10和GTX 16系列显卡 将支持DRX光追技术...

    老卡福音:NVIDIA GeForce GTX 10和GTX 16系列显卡 将支持DRX光追技术4月更新驱动 2019-03-19 13:33:18 23点赞 27收藏 61评论 *级游戏性能请看#攒 ...

  6. 【机器学习系列】之支持向量回归SVR

    作者:張張張張 github地址:https://github.com/zhanghekai [转载请注明出处,谢谢!] [机器学习系列]之SVM硬间隔和软间隔 [机器学习系列]之SVM核函数和SMO ...

  7. 硬件扫盲系列-存储器

    硬件扫盲系列-存储器 1. 前言 2.存储器 2.1 什么是存储器? 2.2 存储器的分类 2.2.1 ROM 2.2.2 RAM 3. ROM 3.1 PROM 3.2 EEPROM 3.3 Fla ...

  8. STM32F407单片机通用24CXXX读写程序(KEIL),兼容24C系列存储器(24C01到24C512),支持存储器任意地址跨页连续读写多个页

    一.AT24CXXX容量   AT24C01,AT24C02,AT24C04,AT24C08,AT24C16,AT24C32,AT24C64,AT24C128,AT24C256-不同的xxx代表不同的 ...

  9. 20分钟充满!华为P50系列或最高支持100W超级快充

    根据官方日前公布的消息,华为将于7月29日19点30分举办新品发布会,届时大家期待已久的全新华为P50系列终于将正式与大家见面.随着发布时间的日益临近,关于该机外观和配置的爆料已经非常详尽.现在有最新 ...

最新文章

  1. post传值php取不到数据,post请求中的参数形式和form-data提交数据时取不到的问题...
  2. pytorch 笔记: 扩展torch.autograd
  3. 利用Azure DevOps建设ExcelBDD的持续集成
  4. 【Python】list转str
  5. DHCP服务和NIS服务
  6. 两个特征是独立好还是正相关好_2021考研数学概率典型例题,都给你总结好啦!...
  7. 昆山万象汇机器人_昆山十镇,在售新盘汇总
  8. Vestigium-Google CodeJam 2020资格回合问题1解决方案
  9. python 捕获鼠标点击事件,在Python中的wx.Frame外部捕获鼠标事件
  10. python 模块 类 函数_Python17之函数、类、模块、包、库
  11. 尝试登录 VMware vCenter Server 5.5 时,“别名”值为空如何解决
  12. 模板引擎thymeleaf和freemarker
  13. 将日期转换成大写例如:二零一三年十二月
  14. pythonQQ机器人系列:使用requests实现QQ机器人聊天(0-2)
  15. linux生成一个ssh密钥,ssh密钥生成方式
  16. 微信逆向:如何统计好友添加数据和聊天记录数据?
  17. simulink AWGN信道使用要点
  18. 【入门教程】必看!TensorFlow中文教程:机器学习从零到一
  19. 如何使用xbrowser图形化连接centos
  20. 一天做100张图?只有 AI 能做到!

热门文章

  1. 多线程下使用Jedis
  2. android ART编译预优化
  3. 【OF框架】使用OF.WinService项目,添加定时服务,进行创建启动停止删除服务操作...
  4. 2017-06-08 前端日报
  5. RSS - 简单方便的follow资讯
  6. EBCDIK,EBCDIC,ASCII,shift JIS間の変換
  7. 《推荐系统实践》要点思维导图
  8. 【正一专栏】巴萨耗光了所有的激情和精力
  9. 机器学习知识点(十)马尔可夫链
  10. Ubuntu15.10安装XAMPP