收集整理了一份《2024年最新物联网嵌入式全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升的朋友。
一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人
都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
很明显,当传感器ADDR引脚接VSS时,采用地址A;当传感器ADDR引脚接VDD时,采用地址B。本次开发的传感器 Address=0x44.
传感器支持单次数据采集模式和周期性数据采集模式。其实单次数据采集模式下,可选时钟延伸,而周期性数据采集默认开始数据延伸。这里我们默认采用周期性数据采集模式。
SHT3X支持12种工作模式,分别有高,中,低三档可选刷新率。mps=0.5,1,2…时,分别代表每两秒采集一次数据,每秒采集一次数据,每秒采集两次数据…
如希望设定高刷新率,每秒采集一次。那么向传感器写命令0x2130即可。注意当采用mps=10时,会导致传感器自发热,影响测量。
工作顺序为:先发送IIC通信开始标志Start后,写入左移一位的地址,并将空出来的位写0表示写数据。当收到传感器应答后,即可发送命令的高八位,再次等待应答,再发送余下的低八位。然后等待ACK应答即可。
其他命令同理,大部分都是同样的写入模式。
1.4 重要命令及其工作流程
那么我们看几条重要的命令及其工作流程。
设置好工作模式后写入此命令,可以准备好接受数据。先发送IIC通信开始标志Start后,写入左移一位的地址,并将空出来的位写1表示读数据。然后等待ACK应答即可接受数据。注意数据传输顺序是先温度后湿度。并且都是十六位数据。并且每个数据后都附8位的CRC校验。在完成湿度的CRC校验后,即可回复NACK,传感器将停止发送数据,释放SDA线,以便于MCU发送Stop标志,结束通信。
第2部分 SHT3x官方参考代码简介
2.1.官方给的参考code是基于STM32平台的。文末有资料获取方式。
由于Nordic蓝牙芯片的I2C接口与STM32有稍微不同。本文将基于该参考code,通过修改,移植到nRF52832蓝牙芯片上。
2.2 参考I2C 代码详解
本文档包含C语言的示例代码,用于通过与SHT3x湿度和温度传感器通信
I2C接口。代码的目的是在实现SHT3x传感器时简化用户的软件编程。除了
简单的测量湿度和温度,代码包含计算CRC校验和和计算物理湿度和温度值。这个示例代码是为STM32-Discovery板编写和优化的,但它可以很容易地应用于其他微控制器而做稍微改变。
至于更详细的官方参考code和说明,不是本文的重点,系列资料在文末有获取方式。
第3部分 Nordic蓝牙芯片 I2C(TWI)软件设计介绍:
关于nRF52832蓝牙芯片的I2C(TWI)串行总线的原理,之前的文章已有介绍。以下主要介绍软件设计部分。
nRF52832 片内集成的 TWI(两线串行总线)兼容 I2C 总线,带有 EasyDMA,可与连接到同一总线的多个从机设备通讯,主要特点如下:
(1)兼容 I2C。
(2)速率:100 kbps、250 kbps 或 400 kbps。
(3)支持时钟延伸。
(4)带 EasyDMA。
(5)TWI 的 SCL 和 SDA 信号可以通过配置寄存器连接到任何一个GPIO,这样可以灵活地实现器件引脚排列,并有效利用电路板空间和信号路由。
nRF52832 的 TWI 的原理框图如下图所示,TWI 主机通过触发STARTTX 或STARTRX 任务启动TWI 传输,通过触发 STOP 任务停止 TWI 传输。TWI 主机在挂起时无法停止,因此必须在 TWI 主机恢复后触发 STOP 任务停止 TWI。启动 TWI 主机后,在TWI 主机停止之前,即在 LASTRX,LASTTX 或 STOPPED 事件之后,不应再次触发 STARTTX 任务或STARTRX 任务。如果从机产生 NACK 输入,那么 TWI 主机将产生ERROR 事件。
3.1.nRF的I2C 函数库应用
TWI 的应用步骤如下图所示,首先要定义 TWI 驱动程序实例,驱动程序实例对应具体的硬件 TWI 外设(TWI0 和 TWI1),驱动程序实例决定了我们使用的是 TWI0 还是 TWI1。接着初始化配置TWI 连接的引脚和速率等参数,注册事件句柄(非堵塞模式下),初始化后使能TWI,之后就可以使用 TWI 进行传输数据。
3.2定义TWI 驱动程序实例
TWI 驱动程序实例使用 nrf_drv_twi_t 结构体定义,该结构体描述了具体的 TWI 外设, 当我们定义了nrf_drv_twi_t 类型的变量并对其赋值后,该变量就对应了一个具体的硬件 TWI 外设。
定义驱动程序实例代码如下,初始化宏 NRF_DRV_TWI_INSTANCE 的输入参数对应TWI 外设的编号,即如果我们定义 TWI0 的驱动程序实例,TWI_INSTANCE_ID 的值设置为 0,定义 TWI1 的驱动程序实例,TWI_INSTANCE_ID 的值设置为 1。驱动程序实例定义后,我们即可通过该驱动程序实例访问对应的 TWI。
3.3 初始化TWI(I2C)
TWI 初始化的库函数是 nrf_drv_twi_init ()函数,该函数同时也配置了 TWI 是否使用阻塞模式。
(1)应用程序提供事件句柄:TWI 工作于非阻塞模式。
(2)应用程序不提供事件句柄(event_handler 设置为 NULL):TWI 工作于阻塞模式。建议使用阻塞模式。
/** TWI初始化
* @brief TWI initialization.
*/
//I2C引脚
#define TWI_SCL_M 26 //I2C SCL
#define TWI_SDA_M 25 //I2C SDA
/* TWI instance. */
static const nrf_drv_twi_t m_twi = NRF_DRV_TWI_INSTANCE(TWI_INSTANCE_ID);
void twi_init (void)
{
ret_code_t err_code;
const nrf_drv_twi_config_t twi_config = {
.scl = TWI_SCL_M,
.sda = TWI_SDA_M,
.frequency = NRF_DRV_TWI_FREQ_100K,
.interrupt_priority = APP_IRQ_PRIORITY_HIGH,
.clear_bus_init = false
};
err_code = nrf_drv_twi_init(&m_twi, &twi_config, NULL, NULL);
APP_ERROR_CHECK(err_code);
nrf_drv_twi_enable(&m_twi);
NRF_LOG_INFO("twi_master_init ok.");
NRF_LOG_FLUSH();
nrf_gpio_pin_clear(20); //ledµÆÁÁ
}
3.5 TWI数据传输函数
TWI 驱动程序提供了两个单独的函数nrf_drv_twi_tx() 函数 和 nrf_drv_twi_ rx() 函数 , 分别用于完成数据的发送和接收,函原型如下表所示。使tx 和 rx 函数时,尤其要注意从机地址 address,函数接收的是 7位地址,函数内部会自己加上读写位。因此如果某个 I2C 接口设备提供的是 8位地址,我们需要提取出 7位地址赋值给address,也就是去掉 8位地址的最低(R/W位)。
nRF52832蓝牙芯片的所有I2C 通信都是通过这两个函数接口。不需要自己去重写I2C驱动函数。
3.5.1 TWI发送函数模型:nrf_drv_twi_tx()
| 函数原型 | STATIC_INLINE ret_code_t nrf_drv_twi_tx (nrf_drv_twi_t const * p_instance, uint8_t address, uint8_t const * p_data, uint8_t length, bool no_stop) |
| 函数功能 | 向 TWI 从机发送数据,发生错误时将停止传输。 如果传输正在进行,则该函数返回错误代码 NRF_ERROR_BUSY。 |
| 参 数 | [in] p_instance:指向 TWI 驱动程序实例结构体。 [in] address:指定的从机地址(7 位 LSB)。 [in] p_data:指向传输数据缓存。 [in] length:发送的字节数。 [in] no_stop:如果置位,成功传输后总线上不会生成停止条件(允许在 下一次传输中重复启动)。 |
| 返回值 | NRF_SUCCESS:发送成功。 NRF_ERROR_BUSY:驱动程序尚未准备好进行新的传输。NRF_ERROR_INTERNAL:硬件检测到错误。NRF_ERROR_INVALID_ADDR:使用了 EasyDMA,但是缓存地址没有位于RAM 空间。 NRF_ERROR_DRV_TWI_ERR_ANACK:在轮询模式下发送地址后收到NAC。 NRF_ERROR_DRV_TWI_ERR_DNACK:在轮询模式下发送数据后收到 NACK。 |
3.5.2 TWI接收函数模型:nrf_drv_twi_rx()
| 函数原型 | STATIC_INLINE ret_code_t nrf_drv_twi_rx ( nrf_drv_twi_t const * p_instance, uint8_t address, uint8_t * p_data, uint8_t length ) |
| 函数功能 | 从 TWI 从机读取数据,发生错误时将停止传输。如果传输正在进行,则该函数返回错误代码 NRF_ERROR_BUSY。 |
| 参 数 | [in] p_instance:指向 TWI 驱动程序实例结构体。 |
|
|
| 返回值 | [in] address:指定的从机地址(7 位 LSB)。 [in] p_data:指向接收数据缓存。 [in] length:读取的字节数。 |
| 返回值 | NRF_SUCCESS:发送成功。 NRF_ERROR_BUSY:驱动程序尚未准备好进行新的传输。NRF_ERROR_INTERNAL:硬件检测到错误。NRF_ERROR_DRV_TWI_ERR_OVERRUN:接收数据未及时读取,被新接收的数据覆盖。 NRF_ERROR_DRV_TWI_ERR_ANACK:在轮询模式下发送地址后收到NAC。 NRF_ERROR_DRV_TWI_ERR_DNACK:在轮询模式下发送数据后收到 NACK。 |
3.5 I2C 设备扫描
为什么要扫描设备?通过扫描I2C 设备,可以确认设备是否正常工作,以及设备接入是否正常等,还可以通过打印信息,确认设备的 I2C 地址。
/* Number of possible TWI addresses. */
#define TWI_ADDRESSES 127
void iic_scan_address(void)
{
ret_code_t err_code;
uint8_t address;
uint8_t sample_data;
bool detected_device = false;
NRF_LOG_INFO("TWI scanner started.");
NRF_LOG_FLUSH();
twi_init();//twi init
for (address = 1; address <= TWI_ADDRESSES; address++)
{
err_code = nrf_drv_twi_rx(&m_twi, address, &sample_data, sizeof(sample_data));
if (err_code == NRF_SUCCESS)
{
detected_device = true;
NRF_LOG_INFO("TWI-i2c device detected at address 0x%x.", address);
}
NRF_LOG_FLUSH();
}
if (!detected_device)
{
NRF_LOG_INFO("No device was found.");
//NRF_LOG_FLUSH();
}
NRF_LOG_INFO("TWI device scan ended.");
NRF_LOG_FLUSH();
}
I2C设备扫描的Log信息:
由于我的I2C 总线上挂了两个设备:
0x3C为 0.96寸oled屏幕的I2C地址,
0x44为,SHT35温湿度传感器的I2C地址。
第4部分 SHT3x代码移植到Nordic蓝牙芯片:
由于Nordic蓝牙芯片的I2C接口与STM32有稍微不同。本文将基于官方给的STM32代码,通过修改,移植到nRF52832蓝牙芯片上。
通过扫描,我知道我的SHT35温湿度传感器的I2C地址为0x44。当然也可以通过手册得知。扫描只是为了确认没错。
4.1 写SHT35寄存器函数
这个函数非常重要,由于SHT35的命令都是16位,而nrf_drv_twi_tx()函数中,数据定义的是8位。所以关键点就在code中。
static uint8_t tx_buf[2];
tx_buf[0] = (uint8_t)(cmd>>8); //低8位放在buf0
tx_buf[1] = (uint8_t)(cmd & 0xFF); //高8位放在buf1
| static etError SHT3x_WriteCommand(uint16_t cmd) { ret_code_t err_code; static uint8_t tx_buf[2]; tx_buf[0] = (uint8_t)(cmd>>8); //低8位 tx_buf[1] = (uint8_t)(cmd & 0xFF); //高8位 //TWI传输完成标志设置为false //m_xfer_done = false; uint8_t retry_num = 20; do{ err_code = nrf_drv_twi_tx(&m_twi, SHT35_ADDRESS, tx_buf, 2, false); APP_ERROR_CHECK(err_code); retry_num--; //等待TWI总线应答 //UNUSED_VARIABLE(err_code); } while((NRF_SUCCESS != err_code) && (0 < retry_num)); //返回写入成功 return NO_ERROR; } |
4.2 周期测量模式
| //发送命令,周期测量模式,测量频率1Hz void SHT3X_SetPeriodicMeasurement(void) { SHT3x_WriteCommand(CMD_MEAS_PERI_2_H); NRF_LOG_INFO("cmd_meas_peri_2_h"); NRF_LOG_FLUSH(); } |
4.3 读取温湿度数据
| //读取温湿度数据,函数中会校验数据 etError SHX3X_ReadMeasurementBuffer(float* temperature, float* humidity) { etError error; ret_code_t err_code; static uint8_t bytes[6]; //写入命令 SHT3x_WriteCommand(CMD_FETCH_DATA); //0xE000,readout measurements for periodic mode //读出温湿度数据 //m_xfer_done = false; //尝试20次 uint8_t retry_num = 20; do{ err_code = nrf_drv_twi_rx(&m_twi, SHT35_ADDRESS, bytes, 6); APP_ERROR_CHECK(err_code); retry_num--; //等待TWI总线应答 //UNUSED_VARIABLE(err_code); } while((NRF_SUCCESS != err_code) && (0 < retry_num)); //while (m_xfer_done == false){}; //校验温湿度数据 error = SHT3X_CheckCrc(bytes, 2, bytes[2]); if(error == NO_ERROR) { *temperature = SHT3X_CalcTemperature((bytes[0] << 8) | bytes[1]); *humidity = SHT3X_CalcHumidity((bytes[3] << 8) | bytes[4]); } return NO_ERROR; } |
收集整理了一份《2024年最新物联网嵌入式全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升的朋友。
一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人
都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!