本篇博客主要用于记录我使用 nRF51822 采用 IIC 方式驱动 加速度传感器 BMA253 的过程。其中的方法也适用于驱动BMA2x2系列芯片。其中若有解释错误的地方,欢迎广大博友指出。文末的参考链接贴出了部分我学习过程中参考的博客,大家也可以查看阅读一下,因为本篇博客篇幅有限,内容写得也不够全面。
BMA253是一个三轴,low-g传感器,主要用于消费电子产品中。它可以测量3个相互垂直的加速度的值。
BMA253的LGA的封装(12 pin)和SPI/IIC 的接口方式有利于进行项目开发。
BMA253提供的VDDIO工作的范围:1.2V ~3.6V 。
BMA253提供两种供电方式:Vdd直接供电给内部模块 或者 Vddio单独给外部接口供电。
BMA253有六种不同的电源模式,一种 normal mode 和五种低功耗模式:
deep-suspend mode,suspend mode,standby mode,low-power mode1 和 low-power mode2
详细的BMA253的资料可以在立创商城上下载数据手册下来看
博客【传感器】BMA253 数字、三轴加速度传感器写得不错也可以看看。
因为绘制原理图时 BMA253 采用的就是IIC通信方式,所以就得捣鼓一下nRF51822的硬件IIC了。因为nRF51系列提供了丰富的样例,所以如果我们在首次学习的时候选择了正确的样例来学习,那么会事半功倍。我就是最开始没有找到合适的样例,学习起来就特别慢,所以这里我建议大家直接下载官方样例 twi_sensor_pca10028 来进行学习开发与移植。具体的样例下载方法可以参考我同系列的笔记三中有详细说明。
I2C通信需要两条线:SDA,SCL。I2C通信设备有两种角色:master和slave,一般用户开发程序都是开发master端,然后去读写作为slave的外设,比如:eeprom,flash,sensor,display device。
在通信过程中,要注意两组特殊控制信号:
start :scl为高电平时,sda由高电平变为低电平。
stop: scl为高电平时,sda由低电平变为高电平。
(注意在通信过程中,SCL始终由master控制,这句话在做模拟I2C的时候就显得意义非凡了)
具体的IIC通信时序可以参考BMA253数据手册上提供的,博客nRF52832之硬件I2C中把IIC通信过程也讲解的很透彻了。
兼容I2C协议
时钟频率有100k,250k,400k可以选择
支持时钟扩展
支持简易DMA
首先我们从主函数开始看起,主函数里面代码也很简单。串口初始化部分这里就不提了,这里使用串口主要是为了方便观看通信过程的数据。
twi_sensor 示例代码演示了如何通过IIC接口与accelorometer(MMA7660FC)通信来。数据从加速度计收集并由UART发送。
/* TWI instance. */
static const nrf_drv_twi_t m_twi_mma_7660 = NRF_DRV_TWI_INSTANCE(0);
int main(void)
{
uart_config();
int a = __GNUC__, c = __GNUC_PATCHLEVEL__;
printf("\n\rTWI sensor example\r\n");
twi_init();
MMA7660_set_mode();
uint8_t reg = 0;
ret_code_t err_code;
while(true)
{
nrf_delay_ms(100);
/* Start transaction with a slave with the specified address. */
do
{
__WFE();
}while(m_xfer_done == false);
err_code = nrf_drv_twi_tx(&m_twi_mma_7660, MMA7660_ADDR, ®, sizeof(reg), true);
APP_ERROR_CHECK(err_code);
m_xfer_done = false;
}
}
void twi_init (void)
{
ret_code_t err_code;
const nrf_drv_twi_config_t twi_mma_7660_config = {
.scl = ARDUINO_SCL_PIN,
.sda = ARDUINO_SDA_PIN,
.frequency = NRF_TWI_FREQ_100K,
.interrupt_priority = APP_IRQ_PRIORITY_HIGH
};
err_code = nrf_drv_twi_init(&m_twi_mma_7660, &twi_mma_7660_config, twi_handler, NULL);
APP_ERROR_CHECK(err_code);
nrf_drv_twi_enable(&m_twi_mma_7660);
}
twi_init()函数中包含了对SCL、SDA引脚端口的初始化,我们可以根据自己的实际电路修改这里的引脚配置。然后就是IIC的时钟频率和中断等级的配置。
再调用 nrf_drv_twi_init()对TWI实例进行初始化,传入的第一个参数是TWI实例结构体、第二个是参数配置数组地址、第三个参数是中断回调函数入口,第四个参数传递给事件处理程序的上下文。
其中第一个参数定义是:NRF_DRV_TWI_INSTANCE(0) 中传入的数字0表示当前使用的TWI0,nRF51822中有两个TWI总线可以使用,若是我们要使用TWI1,则需要定义TWI实例=NRF_DRV_TWI_INSTANCE(1),并且找到 nrf_drv_config.h 开启TWI1
#define TWI1_ENABLED 0 //0 表示关闭 1表示开启
最后调用nrf_drv_twi_enable() 使能当前TWI实例,若有需要我们可以调用 nrf_drv_twi_disable()函数禁用TWI。
紧接着示例代码开始设置MMA7660的运行模式,通过代码注释可以得知,我们需要将MMA7660的模式寄存器置1才能使能该传感器。这里调用了nRF51中TWI通信提供给我们的API接口函数 nrf_drv_twi_tx()将运行模式值(也就是1)写入模式寄存器。
查看nrf_drv_twi_tx()函数功能描述:发送数据给TWI slave 。传入五个参数,分别是TWI实例结构体、slave设备地址、传送的数据缓存指针、数据长度、停止位标识(true表示没有停止位、false表示有停止位)
因此这里设置MMA7660模式的过程是:通过TWI 总线先将模式寄存器地址写入slave 设备,然后将数据(1)写入该寄存器,最后写入停止位表示通信结束。
/**
* @brief Function for setting active mode on MMA7660 accelerometer.
*/
void MMA7660_set_mode(void)
{
ret_code_t err_code;
/* Writing to MMA7660_REG_MODE "1" enables the accelerometer. */
uint8_t reg[2] = {MMA7660_REG_MODE, ACTIVE_MODE};
err_code = nrf_drv_twi_tx(&m_twi_mma_7660, MMA7660_ADDR, reg, sizeof(reg), false);
APP_ERROR_CHECK(err_code);
while(m_set_mode_done == false);
}
与nrf_drv_twi_tx()函数对应的函数是 nrf_drv_twi_rx()函数功能是从TWI slave设备读取数据。传入四个参数,分别是TWI实例结构体、slave设备地址、存储读取数据的接受缓存指针、接受数据的长度。
从while(1)之前的代码可以得出,我们要通过TWI驱动IIC设备,需要的操作首先就是IIC引脚的初始化及TWI功能的使能,紧接着就是使能连接的IIC设备,准备工作做好了之后,就可以进行数据传输了。
while(1)循环中不断给slave设备发送数据,从而触发TWI事件中断(即不断触发调用twi_handler函数),在这个事件中断中再处理接收数据的相关操作。
通过对样例代码的分析我大概知道了如何使用nRF51822的TWI来驱动IIC设备,然后我就着手开始将示例代码中的MMA7660替换成我所使用的BMA253加速度传感器。
阅读BMA253的数据手册可以得知,BMA253的模式寄存器为0x00,正常模式normal_mode值为0。因此在设置好IIC通信引脚端口后,将BMA253的模式寄存器通过nrf_drv_twi_tx()接口函数写入0,在示例代码的基础上我们就可以得到一组从传感器上获取的数据。不过这不是我所需要的,我需要的是从BMA253传感器上读取到X、Y、Z三个方向的加速度值。
还好我找到了BMA2x2系列的驱动代码(BMA2x2_driver)。将驱动代码下载下来之后,只需要将bma2x2.c bma2x2.h添加到自己的工程目录下即可,文件中的bma2x2_support.c文件主要是提供了使用BMA2x2系列传感器的样例代码,其中包括IIC和SPI两种通信模式。
通过阅读bma2x2_support.c中的data_readout_template()读取传感器数据的示例函数可以得知,这里我们初始化IIC通信条件下的BMA253需要几个操作
I2C_routine();//这个函数主要是初始化bma2x2结构体的参数
com_rslt = bma2x2_init(&bma2x2);//初始化总线读取和总线写入功能分配芯片ID和器件地址芯片ID读取寄存器
com_rslt += bma2x2_set_power_mode(BMA2x2_MODE_NORMAL);//设置bma2x2的运行模式
不过这些只是BMA253软件方面的初始化,在这些初始化步骤之前还需要再加上对硬件IIC端口的初始化,也就是twi_sensor示例代码中的twi_init()函数的操作。
然后我将support.c中关于IIC读写的函数和定义全部都复制过来,代码编译通过,下载运行,原以为就能够读取数据,但是结果是什么也没有。然后我再反过来查看代码,原来是忘记在函数BMA2x2_I2C_bus_write()和BMA2x2_I2C_bus_read()中添加相关的读写操作了。
我们已经清楚了如何使用nRF51822的TWI对slave设备进行读写操作,但是不同的芯片的IIC通信的读写时序可能不同,所以这里我们一定要阅读BMA253的数据手册呀,一定要阅读数据手册呀!
查看数据手册上给出的IIC写时序如下
从时序图中可以看出,我们如果要给slave写数据的话,需要先写slave地址,然后是待写入数据寄存器的地址,再接着才是写入的数据,最后是停止位。需要注意的是这里的slave_addr是7位的从设备地址,最低位的读写位不需要你管, 观察上图可以知道这里的slvave address是0x18。后面的RW位为0表示写,因此写从设备代码如下:
s8 BMA2x2_I2C_bus_write(u8 dev_addr, u8 reg_addr, u8 *reg_data, u8 cnt)
{
//BMA253 IIC写时序
//先写从机设备地址---->待写入数据的寄存器地址---->数据....---->停止位
s32 iError = BMA2x2_INIT_VALUE;
u8 array[I2C_BUFFER_LEN];
u8 stringpos = BMA2x2_INIT_VALUE;
array[BMA2x2_INIT_VALUE] = reg_addr;//待写入数据的寄存器地址
for (stringpos = BMA2x2_INIT_VALUE; stringpos < cnt; stringpos++) {
array[stringpos + BMA2x2_BUS_READ_WRITE_ARRAY_INDEX] =
*(reg_data + stringpos);
}
m_xfer_done = false;
iError = nrf_drv_twi_tx(&m_twi_bma253,dev_addr,array,cnt+1,false);//停止位
APP_ERROR_CHECK(iError);
while(m_xfer_done == false);
return (s8)iError;
}
查看数据手册上给出的IIC读时序如下:
这里的读从设备的时序跟平时见到的可能不太一样,这里需要首先将slave地址和待读取数据的寄存器地址写入slave从设备(时序图上RW位为0表示写)。注意后面没有停止位
然后紧接着再进行读取数据的操作,此时读取数据就只需要知道slave从设备地址即可,数据读取完成之后再加上停止位,表示本次通信结束。
因此读时序代码如下:
s8 BMA2x2_I2C_bus_read(u8 dev_addr, u8 reg_addr, u8 *reg_data, u8 cnt)
{
//注意BMA253 IIC读时序
//先发从机设备地址-->再发寄存器地址--->无停止位
//再发从机设备地址-->读数据.......---->读取完成停止
s32 iError = BMA2x2_INIT_VALUE;
m_xfer_done = false;
iError = nrf_drv_twi_tx(&m_twi_bma253,dev_addr,®_addr,1,true);//没有停止位
APP_ERROR_CHECK(iError);
while(m_xfer_done == false);
m_xfer_done = false;
iError = nrf_drv_twi_rx(&m_twi_bma253,dev_addr,reg_data,cnt);
APP_ERROR_CHECK(iError);
while(m_xfer_done == false);
return (s8)iError;
}
将读写操作的代码都添加完整之后,再调用BMA2x2驱动代码中提供的读取xyz方向的加速度接口函数,就可以得出加速度数据。然后再根据我们选用的量程进行单位的转换或者是相关算法的计算,就能得到我们想要的数据。bma2x2.c中给我们提供了很多接口函数。
BMA2x2_driver
nRF51822--TWI(硬件IIC)
Nordic 系列芯片讲解五(Nordic sdk中nrf_drv_twi的使用)
nrf52832用IIC驱动陀螺仪MPU6050
nRF52832之硬件I2C
【传感器】BMA253 数字、三轴加速度传感器