兆易GD32H759I-EVAL 使用逻辑分析仪辅助分析IIC协议点亮BH1750
[复制链接]
本帖最后由 御坂10032号 于 2024-6-8 00:43 编辑
题外话
本章我们将要测评的是GD32的IIC功能,并且做一个小Demo。 使用逻辑分析仪来辅助分析IIC协议并且点亮BH1750光照传感器
正文
因为板子上并没有焊接排针,所以你想要使用逻辑分析仪的时候需要焊接2.0MM的排针。 并且根据数据手册的PIN定义找到你支持IIC功能的PIN。
接线图如下,分别是将开发板的PF14和15 接到BH1750的SCL 和 SDA上,然后逻辑分析仪和BH1750共地。并且连接SCL和SDA用来捕获信号。
根据BH1750的数据手册得知 BH1750的IIC地址存在两种情况,分别是H 和 L。 当ADDR 引脚被接到H的话,那么它的地址就是0x46, 如果接到L引脚的话,那么它的地址就是0x23
根据官方数据手册提供的example我们可以得知,首先发送地址+写, 然后设置BH1750的分辨率。 之后停止便可以转换为读模式。 通过读模式我们便可以读取到BH1750的值
接下来便开始编码部分。
编码
昨天下午2点开始看GD32的这个IIC库的代码和手册,一直折腾到现在,发现这个库的代码支持的功能比较多。也比较乱。很让人难懂。
手册上的主机发送流程也比较难懂,主要是发送后的这个标志位判断有点难以理解。我来给大家对比一下。
GD32的流程
STM32F429的
在这一点上我建议厂商能够将每次传送后触发的判断事件,以类似上图的这种方式罗列出来,这样的话参考事件和官方的库函数可以非常快的来开发基于IIC的应用。
同时,官方提供的Demo,比较难懂(GD32H7xx_Firmware_Library_V1.2.0 下的example 和GD32H759I_EVAL_Demo_Suites 下的Eeprom)代码使用状态机的方式保证了程序的健壮性。但是可读性较差。而且其中某些Flag的判断并没有使用库函数的方式,而是直接操作寄存器。更加增加阅读难度。
读取BH1750光照传感器代码如下
#include "gd32h7xx.h"
#include "systick.h"
#include "bsp_usart.h"
// I2C和BH1750相关定义
#define I2C_SPEED 400000
#define RCU_GPIO_I2C_SDA RCU_GPIOF
#define RCU_GPIO_I2C_SCL RCU_GPIOF
#define RCU_I2C RCU_I2C3
#define I2C_SCL_PORT GPIOF
#define I2C_SDA_PORT GPIOF
#define I2C_SCL_PIN GPIO_PIN_14
#define I2C_SDA_PIN GPIO_PIN_15
#define I2C_GPIO_AF GPIO_AF_4
#define I2C_TIME_OUT (uint32_t)(50000)
#define EEP_FIRST_PAGE 0x00
#define I2C_OK 0
#define I2C_FAIL 1
#define BH1750_ADDRESS 0x46
#define BH1750_POWER_ON 0x01
#define BH1750_RESET 0x07
#define BH1750_HRES_MODE 0x10
void cache_enable(void)
{
/* Enable I-Cache */
SCB_EnableICache();
/* Enable D-Cache */
SCB_EnableDCache();
}
void gpio_config(void)
{
/* enable I2C_SCL_PIN clock */
rcu_periph_clock_enable(RCU_GPIO_I2C_SCL);
/* enable I2C_SDA_PIN clock */
rcu_periph_clock_enable(RCU_GPIO_I2C_SDA);
/* enable I2C clock */
rcu_periph_clock_enable(RCU_I2C3);
/* connect I2C_SCL_PIN to I2C_SCL */
gpio_af_set(I2C_SCL_PORT, I2C_GPIO_AF, I2C_SCL_PIN);
/* connect I2C_SDA_PIN to I2C_SDA */
gpio_af_set(I2C_SDA_PORT, I2C_GPIO_AF, I2C_SDA_PIN);
/* configure GPIO pins of I2C */
gpio_mode_set(I2C_SCL_PORT, GPIO_MODE_AF, GPIO_PUPD_PULLUP, I2C_SCL_PIN);
gpio_output_options_set(I2C_SCL_PORT, GPIO_OTYPE_OD, GPIO_OSPEED_60MHZ, I2C_SCL_PIN);
gpio_mode_set(I2C_SDA_PORT, GPIO_MODE_AF, GPIO_PUPD_PULLUP, I2C_SDA_PIN);
gpio_output_options_set(I2C_SDA_PORT, GPIO_OTYPE_OD, GPIO_OSPEED_60MHZ, I2C_SDA_PIN);
}
void i2c_config(void)
{
/* configure the I2C1 clock source selection */
rcu_i2c_clock_config(IDX_I2C3, RCU_I2CSRC_IRC64MDIV);
/* configure I2C timing */
i2c_timing_config(I2C3, 0x0, 0x6, 0);
i2c_master_clock_config(I2C3, 0x26, 0x73);
/* enable I2C */
i2c_enable(I2C3);
}
/*
*@desc: 发送数据
*@parameter 1: IIC外设
*@parameter 2: 从机地址
*@parameter 3: 传送的Buffer
*@parameter 4: 传送的字节数
*@return :
*/
void send_data(uint32_t i2c_periph, uint8_t address, uint8_t *p_buffer, uint16_t number_of_byte) {
uint32_t timeout = 0;
i2c_master_addressing(i2c_periph, address, I2C_MASTER_TRANSMIT);
i2c_transfer_byte_number_config(i2c_periph, number_of_byte);
i2c_automatic_end_enable(i2c_periph);
while(i2c_flag_get(i2c_periph, I2C_FLAG_I2CBSY) && (timeout < I2C_TIME_OUT)) {
timeout++;
}
if(timeout < I2C_TIME_OUT) {
i2c_start_on_bus(i2c_periph);
for(uint16_t i = 0; i < number_of_byte; i++) {
i2c_data_transmit(i2c_periph, p_buffer[i]);
}
timeout = 0;
while(!i2c_flag_get(i2c_periph, I2C_FLAG_TBE) && (timeout < I2C_TIME_OUT)) {
timeout++;
}
} else {
printf("i2c bus is busy in write!\n");
}
}
/*
*@desc: 读取数据到p_buffer 中
*@parameter 1: IIC外设
*@parameter 2: 从机地址
*@parameter 3: 传送的Buffer
*@parameter 4: 传送的字节数
*@return :
*/
void read_data(uint32_t i2c_periph, uint8_t address, uint8_t *p_buffer, uint16_t number_of_byte) {
uint32_t timeout = 0;
i2c_master_addressing(i2c_periph, address, I2C_MASTER_RECEIVE);
i2c_transfer_byte_number_config(i2c_periph, number_of_byte);
i2c_automatic_end_enable(i2c_periph);
while(i2c_flag_get(i2c_periph, I2C_FLAG_I2CBSY) && (timeout < I2C_TIME_OUT)) {
timeout++;
}
if(timeout < I2C_TIME_OUT) {
i2c_start_on_bus(i2c_periph);
for(uint16_t i = 0; i < number_of_byte; i++) {
while(!i2c_flag_get(i2c_periph, I2C_FLAG_RBNE) && (timeout < I2C_TIME_OUT)) {
timeout++;
}
if(timeout < I2C_TIME_OUT) {
p_buffer[i] = i2c_data_receive(i2c_periph);
} else {
printf("i2c read timeout!\n");
break;
}
}
timeout = 0;
while(!i2c_flag_get(i2c_periph, I2C_FLAG_STPDET) && (timeout < I2C_TIME_OUT)) {
timeout++;
}
} else {
printf("i2c bus is busy in read!\n");
}
}
int main(void)
{
// 变量定义
uint16_t light_level;
uint8_t read_buffer[2];
uint8_t power_on = BH1750_POWER_ON;
uint8_t high_res_mode = BH1750_HRES_MODE;
// 启用 CPU Cache
cache_enable();
// 配置 systick 和 USART
systick_config();
usart_init();
usart_receive_init();
// 配置 GPIO 和 I2C
gpio_config();
i2c_config();
// 等待 BH1750 上电完成
delay_1ms(10);
// 发送启动指令
send_data(I2C3, BH1750_ADDRESS, &power_on, 1);
delay_1ms(10); // 等待传感器上电完成
while (1) {
// 发送高分辨率模式指令
send_data(I2C3, BH1750_ADDRESS, &high_res_mode, 1);
delay_1ms(300); // 等待传感器完成测量
// 读取数据并计算光照强度
read_data(I2C3, BH1750_ADDRESS, read_buffer, 2);
light_level = (read_buffer[0] << 8) | read_buffer[1];
float lux = light_level / 1.2;
printf("Light intensity: %.2f lux\n", lux);
}
}
逻辑分析仪采样的写
逻辑分析仪采样的读
实验效果
WeChat_20240608003734
代码如下
额外补充
逻辑分析仪是个好东西,特别是在某些情况你并不确定你代码写的是否正常并且是否正常输出的时候,可以非常轻松的用逻辑分析仪捕获输出情况从而来调整代码。要求不高的话可以使用树莓派
|