nRF52832外设I2C的使用说明(附MAX44009光感传感器驱动代码)

nRF52832外设I2C的使用说明

  • 背景
  • I2C原理
    • 本文的I2C使用环境
      • 开发环境
      • 芯片型号
      • SDK版本
      • 使用例程
      • 传感器详情
    • I2C移植
      • 添加文件到ble_app_uart例程
      • 添加头文件包含路径
        • 在文件中添加引用
        • 设置头文件包含路径
      • 编写测试代码
    • 总结及源码
      • 调试过程中遇到的问题
      • 源代码链接

背景

I2C是比较常用的通信接口,nRF52832的I2C它自己封装了一下变成:TWI(Two Write Interface),在它的SDK中有例程供我们学习,本文是基于twi_sensor和ble_app_uart例程完成的MAX44009光感传感器的数据读取,在移植twi驱动时遇到了很多问题,搜索网上一些资料感觉他们写的太简单,重要的的步骤比如:添加什么文件、需要配置什么文件等等,都没有说(可能是自己没有理解其中的深意吧!)所以自己决心写一个针对nRF52832的I2C的新手使用文档,希望可以帮助广大和我一样处于初学者的程序员们。

I2C原理

I2C的原理网上有一大堆资料,这里我说下自己对I2C的认识:
·1、I2C数据传输有2根线
SDA(数据线)和SCL(时钟线)。
·2、空闲状态
I2C的SDA和SCL在空闲时处于高电平。
·3、起始停止信号
起始条件是数据线在时钟高电平时由高电平向低电平跳变,停止条件是数据线在时钟高电平时由低电平向高电平跳变。
nRF52832外设I2C的使用说明(附MAX44009光感传感器驱动代码)_第1张图片
·4、字节传输格式
I2C 总线以字节为单位收发数据。传输到SDA线上的每个字节必须为8位。每次传输的字节数量不受限制。首先传输的是数据的最高位(MSB 第7位),最后传输的是最低位(LSB,第0位)。另外每个字节后必须跟一个响应位(ACK)。I2C传输数据如图2所示。
图2 I2C 总线的数据传输
nRF52832外设I2C的使用说明(附MAX44009光感传感器驱动代码)_第2张图片
·5、ACK信号
每个字节后会跟一个bit,这就是ACK信号(高电平是NACK、低电平是ACK),注意在结束信号前的ACK信号是高电平。
·6、从机地址
I2C总线不需要额外的地址译码器和片选信号。多个具有I2C总线接口的器件都可以连接到同一条I2C总线上,它们之间通过器件地址来区分。I2C 总线的寻址过程通常是在起始条件后的第一个字节决定了主机选择哪一个从机,即7位寻址地址(另一种是10位寻址地址,有所不同,本传感器采用7位寻址地址)。第一个字节的位定义如图3所示,第一个字节的头7 位组成了从机地址,最低位(LSB)是第8位。它决定了报文的方向,第一个字节的最低位(LSB)是“0”:表示主机会写信息到被选中的从机;“1”表示主机会向从机读信息。
nRF52832外设I2C的使用说明(附MAX44009光感传感器驱动代码)_第3张图片
图3 起始条件后的第一个数据
·7、基本数据传输格式
图4、图5分别给出了I2C的发送和接收数据的基本格式。应当注意的是,图4和图5情况不同的是,在图4中,主机在向从机发送最后一个字节的数据时,从机可能应答也可能非应答,但不管怎样主机都可以产生停止条件。如果主机在向从机发送数据(甚至包括从机地址在内)时检测到从机非应答,则应当及时停止传输。
nRF52832外设I2C的使用说明(附MAX44009光感传感器驱动代码)_第4张图片
图4 I2C 总线主机向从机发送数据基本格式
nRF52832外设I2C的使用说明(附MAX44009光感传感器驱动代码)_第5张图片
图5 I2C 总线主机从从机接收数据的基本格式

本文的I2C使用环境

开发环境

本文使用的开发环境是:Keil5

芯片型号

nRF52832_xxAA系列

SDK版本

本文使用的SDK版本是:nRF5_SDK_12.3.0_d7731ad

使用例程

因为是在做蓝牙方面的项目时才去看nRF的I2C的所以使用的例程是:ble_app_uart

传感器详情

我使用的光感传感器是:MAX44009,它的器件地址由AO脚选择:高电平(接3.3V)时选择地址1001 011x,低电平(接地)时选择地址1001 010x,这里我选择的是接GND所以器件地址是:0x94(注意有些芯片的API会根据函数(读取/写入)来自动将器件地址左移然后加上0/1位)。

I2C移植

我是参考SDK中的twi_sensor例程来移植I2C到ble_app_uart例程中的,下面是移植的详细过程。

添加文件到ble_app_uart例程

使用TWI程序模块,需要在工程中添加“app_twi.h”和“nrf_drv_twi.h”文件,该文件在SDK中的路径如下。
…\nRF5_SDK_12.3.0_d7731ad\components\drivers_nrf\twi_master\nrf_drv_twi.c
…\nRF5_SDK_12.3.0_d7731ad\components\libraries\twi\app_twi.c
nRF52832外设I2C的使用说明(附MAX44009光感传感器驱动代码)_第6张图片
图6 TWI相关文件
添加文件后,还需要在配置文件“sdk_config.h”中启用 TWI程序模块,即设置宏定义TWI_ENABLED 和APP_TWI_ENABLED以及TWI0_ENABLED为“1”,如下图所示:
nRF52832外设I2C的使用说明(附MAX44009光感传感器驱动代码)_第7张图片
图7 TWI_ENABLED和APP_TWI_ENABLED使能
在这里插入图片描述
图8 TWI0_ENABLED使能

添加头文件包含路径

头文件包含一般分为两个步骤:首先在文件中添加头文件引用,然后在工程配置中设置头文件包含路径。

在文件中添加引用

#include “app_util_platform.h”
#include “app_twi.h”
#include “nrf_drv_twi.h”
#include “math.h”
#include “nrf_delay.h”

设置头文件包含路径

…\components\drivers_nrf\twi_master
…\components\libraries\twi

编写测试代码

#define MAX44009_ADDR (0x94>>1) //光感器件地址
static volatile bool m_xfer_done = false;//指示TWI操作是否结束
static const nrf_drv_twi_t m_twi = NRF_DRV_TWI_INSTANCE(0);//寄存器结构体
这里有三个参数:
·1、MAX44009的器件地址,因为twi的发送接收函数会自动在Bit0上置1或置0,所以将MAX44009的器件地址右移1位。
·2、TWI操作状态指示,无论是写入还是读取都要等待上一次TWI操作完成才能进行所以加了一个标志。
·3、用于创建TWI主驱动程序实例的宏,就是配置使用TWI0还是TWI1,其中需要输入的参数:0(TWI0)/1(TWI1)。

void twi_handler(nrf_drv_twi_evt_t const * p_event, void * p_context)//twi 回调函数,主要是用来上报twi操作完成功能
{
switch (p_event->type)
{
case NRF_DRV_TWI_EVT_DONE:
m_xfer_done = true;
break;
default:
break;
}
}
该函数是TWI的回调函数,各种TWI的消息事件都会在这个回调函数中进行处理。

static void twi_config(void)// TWI初始话配置函数
{
uint32_t err_code;
nrf_drv_twi_config_t const config = {
.scl = ARDUINO_SCL_PIN,//时钟gpio引脚这里我用的是7
.sda= ARDUINO_SDA_PIN,//数据gpio引脚这里我用的是8
.frequency= NRF_TWI_FREQ_100K,//TWI主时钟频率
.interrupt_priority = APP_IRQ_PRIORITY_LOWEST,//中断优先级
.clear_bus_init= false//初始化期间清除总线
};//twi配置结构体
err_code = nrf_drv_twi_init(&m_twi, &config, twi_handler, NULL);
APP_ERROR_CHECK(err_code);
nrf_drv_twi_enable(&m_twi);
}
该函数是TWI的配置、初始化、使能函数。

//读取MAX44009光感传感器数据并解析用uart输出
static void Read_Max44009_Ligth_Intensity(void)
{
uint8_t l_write_register_data=0x04;//低字节寄存器
uint8_t h_write_register_data=0x03;//高字节寄存器
uint8_t read_l_data=0;
uint8_t read_h_data=0;
uint32_t light_intensity_value=0;
uint8_t temp_one=0;//指数
uint8_t temp_two=0;//尾数
m_xfer_done=false;
nrf_drv_twi_tx(&m_twi,MAX44009_ADDR,&l_write_register_data,1,false);
while (m_xfer_done == false);
m_xfer_done = false;
nrf_drv_twi_rx(&m_twi,MAX44009_ADDR,&read_l_data,1);
while (m_xfer_done == false);
//printf("—read L data=%d—\r\n",read_l_data);
m_xfer_done=false;
nrf_drv_twi_tx(&m_twi,MAX44009_ADDR,&h_write_register_data,1,false);
while (m_xfer_done == false);
m_xfer_done = false;
nrf_drv_twi_rx(&m_twi,MAX44009_ADDR,&read_h_data,1);
while (m_xfer_done == false);
//printf("—read H data=%d—\r\n",read_h_data);
temp_one=(read_h_data>>4)&0xFF;
temp_two=((read_h_data<<4)|read_l_data)&0xFF;
light_intensity_value=pow(2,(double)temp_one)temp_two0.045;
printf("—currentlightintensity=%d—\r\n",light_intensity_value);
}
MAX44009的计算公式为: 2(指数)x尾数x0.045

总结及源码

调试过程中遇到的问题

调试I2C花了2天时间,因为之前调试过I2c所以感觉自己调试nRF52832的I2C(TWI)会比较简单,但是实际移植过程中发现了很多问题:
·1、不知道该参考哪一个例程,由于SDK中由好几个TWI的例程对于第一次接触这个平台的人来说无从下手。
·2、SDK中的例程取名过于混乱,就我感觉Nordic把I2C改名为TWI是有误导的,我第一次找I2C的例程愣是没找到,后来别人告诉我才知道TWI就是nRF的I2C,无语了。
·3、Nordic的SDK封装的太深了,不像Stm32可以很轻易的找到底层驱动函数,这增加了阅读难度。
·4、网上没有太多的例程,就算有也不是适合新手阅读的。
·5、sdk_config.h新手不会用

源代码链接

nRF52832驱动MAX44009的代码:

你可能感兴趣的:(嵌入式软件开发)