基于RoboMasterC板的RT-Thread使用分享—i2c读取磁力计数据实验

i2c简介
i2c 是 philips 公司开发的一种半双工、双向二线制同步串行总线。
两线制代表 i2c 只需两根信号线,一根数据线 sda,另一根是时钟线 scl。这个也是i2c的优势所在,虽然传输速率较慢,但是占用引脚数量少,在引脚资源紧张的芯片上就特别好用。
i2c 总线允许挂载多个主设备,但总线时钟同一时刻只能由一个主设备产生,并且要求每个连接到总线上的器件都有唯一的 i2c 地址,从设备可以被主设备寻址。那么我们想要在同一个i2c总线上使用多个主设备。
这里善于思考的同学可能就会想到如果我们想要在同一个i2c总线上并联两个一样的设备id冲突怎么办呢?
之后带大家看传感器的datasheet的时候,就会发现传感器上面有一个addr引脚,根据它引脚电平高低会切换id号,这样就可以避免id冲突了。
i2c通信具有几类信号
开始信号s:当scl处于高电平时,sda从高电平拉低至低电平,代表数据传输的开始
结束信号p:当scl处于高电平时,sda从低电平拉高至高电平,代表数据传输结束
数据信号:数据信号每次都传输8位数据,每一位数据都在一个时钟周期内传递,当scl处于高电平时,sda数据线上的电平需要稳定,当scl处于低电平的时候,sda数据线上的电平才允许改变。
应答信号ack/nack:应答信号是主机发送8bit数据,从机对主机发送低电平,表示已经接收数据。
整个i2c通信过程理解成收发快递的过程,设备i2c地址理解成学校快递柜的地址,读写位代表寄出和签收快递,寄存器地址则是快递柜上的箱号,而数据便是需要寄出或者签收的快递。整个过程便是如同到学校的快递柜(从机 i2c 地址),对第几号柜箱(寄存器地址), 进行寄出或者签收快递(数据)的过程。
ist8310简介
ist8310 是一款由 isentek 公司推出的 3 轴磁场传感器,尺寸为 3.03.01.0mm,支持快速 i2c 通信,可达 400khz,14 位磁场数据,测量范围可达1600ut(x,y-axis)和 2500ut(z-axis), 最高 200hz 输出频率。使用ist8310磁力计可以检测地磁场强度,用于计算磁场角度。
下图为ist8310的引脚功能表
此外我们可以整理等下我们会用到的gpio引脚
以及我们可以看一下从机地址的设置,在这个芯片之后从机地址的设置是通过cad0和cad1两个引脚设置的,这样可以实现设置四个不同的地址。
下面是开发板的原理图,我们可以看到下面cad0和cad1引脚都浮空了,根据上图可以得知,这个ist8310的从机地址为0x0e与原理图中标注的一致。
cubemx配置
首先我们开始看到我们的原理图上。
根据上面两个图我们可以发现这个ist8310是挂载在i2c总线上面的。
stm32上i2c3_scl在pa8,i2c_sda在pc9,drdy数据准备引脚在pg3,复位rstn引脚在pg6,接着在cubemx进行相应的配置。
之后我们可以看到我们在cubemx中配置的东西实际上都在board文件之中,其中board.c文件里面为我们的时钟树配置,stm32f4xx_hal_msp.c里的就是我们上面设置的那些引脚配置
这里就是刚刚i2c3配置的函数,为啥突然开始讲这个主要也是实习时发现大部分情况下是没有stm32可以用的,也就不能用cubemx那么轻松的配置了,pintopin替换直接把***当作stm32使用总感觉会出问题,因此还是学习自己配置的方法。
else if(hi2c->instance==i2c3)
{
/* user code begin i2c3_mspinit 0 /
/ user code end i2c3_mspinit 0 */
__hal_rcc_gpioc_clk_enable();
__hal_rcc_gpioa_clk_enable();
/**i2c3 gpio configuration
pc9 ------> i2c3_sda
pa8 ------> i2c3_scl
/
gpio_initstruct.pin = gpio_pin_9;
gpio_initstruct.mode = gpio_mode_af_od;
gpio_initstruct.pull = gpio_nopull;
gpio_initstruct.speed = gpio_speed_freq_very_high;
gpio_initstruct.alternate = gpio_af4_i2c3;
hal_gpio_init(gpioc, &gpio_initstruct);
gpio_initstruct.pin = gpio_pin_8;
gpio_initstruct.mode = gpio_mode_af_od;
gpio_initstruct.pull = gpio_nopull;
gpio_initstruct.speed = gpio_speed_freq_very_high;
gpio_initstruct.alternate = gpio_af4_i2c3;
hal_gpio_init(gpioa, &gpio_initstruct);
/ peripheral clock enable /
__hal_rcc_i2c3_clk_enable();
/ user code begin i2c3_mspinit 1 /
/ user code end i2c3_mspinit 1 */
}
代码编写
首先在rt-thread settings组件中打开i2c设备驱动程序
menuconfig bsp_using_i2c3
bool enable i2c3 bus (software simulation)
default n
select rt_using_i2c
select rt_using_i2c_bitops
select rt_using_pin
if bsp_using_i2c3
comment notice: pa8 --> 8; pc9 --> 41
config bsp_i2c3_scl_pin
int i2c1 scl pin number
default 8
config bsp_i2c3_sda_pin
int i2c1 sda pin number
default 41
endif
配置完后我们来看一下几个重要的实现函数
i2c写入寄存器的函数
rt_err_t ist8310_iic_write(rt_uint8_t write_addr, rt_uint8_t data, rt_uint32_t number)
{
rt_uint8_t buf[2];
buf[0] = write_addr;
buf[1] = data;
rt_size_t result;
result = rt_i2c_master_send(ist8310_i2c_bus, ist8310_addr, rt_i2c_wr, buf, 2);
rt_thread_mdelay(10);
if (result == 2)
{
rt_kprintf(ist8310 write failed,err is:%drn, result);
return -rt_error;
}
}
关于读取,写入这里引用上面推荐视频里的一幅图
这里我们函数的实现也是按照这个原理,rt_i2c_master_send首先将设备的地址ist8310_addr,加上读写位rt_i2c_wr发送
之后我们定义的buf缓冲区中装着的就是我们要发送的寄存器地址write_addr,和要写入的数据data。
需要注意的是rt_i2c_master_send返回的是发送的消息的个数,且不包含一开始发送的设备地址ist8310_addr的。
下面的读取函数也是同理的,先发送想要读取的read_addr,然后利用rt_i2c_master_recv函数进行读取。
rt_err_t ist8310_iic_read(rt_uint8_t read_addr, rt_uint32_t len, rt_uint8_t *buf)
{
//通知要读哪个设备的哪个内存地址的内容,(告知是需要读read_addr)
rt_i2c_master_send(ist8310_i2c_bus, ist8310_addr, rt_i2c_wr, &read_addr, 1);
//读取到的内容存入buf
rt_i2c_master_recv(ist8310_i2c_bus, ist8310_addr, rt_i2c_rd, buf, len); //地址读数据
}
这样看完大家应该对于i2c的通信方式更加熟悉了。
下面介绍的是读取磁力计值函数ist8310_read_mag
void ist8310_read_mag(float mag[3])
{
uint8_t buf[6];
int16_t temp_ist8310_data = 0;
//read the dataxl register (0x03)
ist8310_iic_read(0x03, 6, buf);
temp_ist8310_data = (int16_t) ((buf[1] << 8) | buf[0]);
mag[0] = mag_sen * temp_ist8310_data;
temp_ist8310_data = (int16_t) ((buf[3] << 8) | buf[2]);
mag[1] = mag_sen * temp_ist8310_data;
temp_ist8310_data = (int16_t) ((buf[5] while (1)
{
tnow = systick->val;
if (tnow != told)
{
if (tnow = ticks)
{
break;
}
}
}
}
rt_err_t ist8310_iic_read(rt_uint8_t read_addr, rt_uint32_t len, rt_uint8_t *buf)
{
//通知要读哪个设备的哪个内存地址的内容,(告知是需要读read_addr)
rt_i2c_master_send(ist8310_i2c_bus, ist8310_addr, rt_i2c_wr, &read_addr, 1);
//读取到的内容存入buf
rt_i2c_master_recv(ist8310_i2c_bus, ist8310_addr, rt_i2c_rd, buf, len); //地址读数据
}
rt_err_t ist8310_iic_write_single_reg(rt_uint8_t reg, rt_uint8_t data)
{
rt_uint8_t buf[2];
buf[0] = reg;
buf[1] = data;
if (rt_i2c_master_send(ist8310_i2c_bus, ist8310_addr, 0, buf, 2) == 2)
{
return rt_eok;
}
else
{
return -rt_error;
}
}
uint8_t ist8310_iic_read_single_reg(rt_uint8_t reg, rt_uint8_t len, rt_uint8_t buf)
{
struct rt_i2c_msg msgs[2];
msgs[0].addr = ist8310_addr; / 从机地址 /
msgs[0].flags = rt_i2c_wr; / 写标志 /
msgs[0].buf = ® / 从机寄存器地址 /
msgs[0].len = 1; / 发送数据字节数 /
msgs[1].addr = ist8310_addr; / 从机地址 /
msgs[1].flags = rt_i2c_rd; / 读标志 /
msgs[1].buf = buf; / 读取数据指针 /
msgs[1].len = len; / 读取数据字节数 /
if (rt_i2c_transfer(ist8310_i2c_bus, msgs, 2) == 2)
{
return rt_eok;
}
else
{
return -rt_error;
}
}
void ist8310_rst_h(void)
{
rt_pin_write(ist8310_rstn_pin_num, 1);
}
void ist8310_rst_l(void)
{
rt_pin_write(ist8310_rstn_pin_num, 0);
}
void ist8310_read_mag(float mag[3])
{
uint8_t buf[6];
int16_t temp_ist8310_data = 0;
//read the dataxl register (0x03)
ist8310_iic_read(0x03, 6, buf);
temp_ist8310_data = (int16_t) ((buf[1] << 8) | buf[0]);
mag[0] = mag_sen * temp_ist8310_data;
temp_ist8310_data = (int16_t) ((buf[3] << 8) | buf[2]);
mag[1] = mag_sen * temp_ist8310_data;
temp_ist8310_data = (int16_t) ((buf[5] << 8) | buf[4]);
mag[2] = mag_sen * temp_ist8310_data;
}
rt_uint8_t ist8310_init(const charname)
{
rt_uint8_t temp[2] = { 0, 0 };
ist8310_i2c_bus = (struct rt_i2c_bus_device *) rt_device_find(name);
rt_uint8_t res[2] = { 0, 0 };
rt_uint8_t writenum = 0;
if (ist8310_i2c_bus == rt_null)
{
rt_kprintf(can't find %s device!n, name);
}
else
{
ist8310_rst_l();
rt_thread_mdelay(sleeptime);
ist8310_rst_h();
rt_thread_mdelay(sleeptime);
ist8310_iic_read_single_reg(ist8310_who_am_i, 1, res);
if (res[0] != ist8310_who_am_i_value)
{
initialized = rt_true;
return ist8310_no_sensor;
}
for (writenum = 0; writenum < ist8310_write_reg_num; writenum++)
{
ist8310_iic_write_single_reg(ist8310_write_reg_data_error[writenum][0],
ist8310_write_reg_data_error[writenum][1]);
ist8310_delay_us(wait_time);
ist8310_iic_read_single_reg(ist8310_write_reg_data_error[writenum][0], 1, res);
ist8310_delay_us(wait_time);
if (res[0] != ist8310_write_reg_data_error[writenum][1])
{
return ist8310_write_reg_data_error[writenum][2];
}
}
initialized = rt_true;
return ist8310_no_error;
}
}
static void i2c_ist8310_sample(int argc, char argv[])
{
rt_uint8_t buf;
rt_uint8_t result=0;
float msg[3] = { 0, 0, 0 };
char name[rt_name_max];
if (argc == 2)
{
rt_strncpy(name, argv[1], rt_name_max);
}
else
{
rt_strncpy(name, ist8310_i2c_bus_name, rt_name_max);
}
if (!initialized)
{
/ 传感器初始化 /
result=ist8310_init(name);
}
if (initialized)
{
ist8310_read_mag(msg);
rt_kprintf(read ist8310 sensor x:%f y:%f z:%fn, msg[0], msg[1], msg[2]);
}
else
{
rt_kprintf(%dn,result);
rt_kprintf(initialize sensor failed!n);
}
}
/ 导出到 msh 命令列表中 */
msh_cmd_export(i2c_ist8310_sample, ist8310_sample);
ist8310.h
//
// created by goldengrandpa on 2022/11/4.
//
#ifndef rtthread_ist8310_h
#define rtthread_ist8310_h
#include
#include
#include board.h
#define ist8310_i2c_bus_name i2c3 /* 传感器连接的i2c总线设备名称 /
#define ist8310_addr 0x0e / 从机地址 /
#define ist8310_calibration_cmd 0xe1 / 校准命令 /
#define ist8310_normal_cmd 0xa8 / 一般命令 /
#define ist8310_get_data 0xac / 获取数据命令 */
#define ist8310_write_reg_num 4
#define ist8310_who_am_i 0x00 //ist8310 who am i
#define ist8310_who_am_i_value 0x10 //device id
#define ist8310_data_ready_bit 2
#define ist8310_no_error 0x00
#define ist8310_no_sensor 0x40
static const uint8_t wait_time = 150;
static const uint8_t sleeptime = 50;
#define ist8310_rstn_pin_num get_pin(g,6)
#define mag_sen 0.3f //raw int16 data change to ut unit. 原始整型数据变成 单位ut
typedef struct ist8310_real_data_t
{
uint8_t status;
float mag[3];
} ist8310_real_data_t;
//the first column:the registers of ist8310. 第一列:ist8310的寄存器
//the second column: the value to be writed to the registers.第二列:需要写入的寄存器值
//the third column: return error value.第三列:返回的错误码
static const rt_uint8_t ist8310_write_reg_data_error[ist8310_write_reg_num][3] ={
{0x0b, 0x08, 0x01}, //enalbe interrupt and low pin polarity.开启中断,并且设置低电平
{0x41, 0x09, 0x02}, //average 2 times.平均采样两次
{0x42, 0xc0, 0x03}, //must be 0xc0. 必须是0xc0
{0x0a, 0x0b, 0x04}}; //200hz output rate.200hz输出频率
static struct rt_i2c_bus_device ist8310_i2c_bus = rt_null; / i2c总线设备句柄 /
static rt_bool_t initialized = rt_false; / 传感器初始化状态 */
rt_err_t ist8310_iic_write(rt_uint8_t write_addr, rt_uint8_t data, rt_uint32_t number);
rt_err_t ist8310_iic_read(rt_uint8_t read_addr, rt_uint32_t len, rt_uint8_t *buf);
void ist8310_read_mag(float mag[3]);
static void read_mag(struct rt_i2c_bus_device *bus,float *cur_mag);/ 读取磁场 /
void ist8310_rst_h(void);/ 设置rstn引脚为1 /
void ist8310_rst_l(void);/ 设置rstn引脚为0 /
void ist8310_delay_us(rt_uint16_t us);
rt_err_t ist8310_iic_write_single_reg(rt_uint8_t reg, rt_uint8_t data);
rt_uint8_t ist8310_iic_read_single_reg(rt_uint8_t reg, rt_uint8_t len, rt_uint8_t *buf);
static void i2c_ist8310_sample(int argc, char *argv[]);
#endif //rtthread_ist8310_h
运行结果:

智能家居的三大优势
小米6或将搭载羲力X30处理器 黑科技越走越远?
薄膜高低温拉力试验机的原理、构造及拉伸试验操作方法,这里都说清楚了!
烟草仓库物流中RFID技术的应用方案
西部数据iNAND AT EM132嵌入式闪存盘专为自动驾驶高级应用设计
基于RoboMasterC板的RT-Thread使用分享—i2c读取磁力计数据实验
紫光展锐与华为合作5G VoNR通话测试
佳能EOSM5相机,小巧机身便于携带拥有2420万的有效像素
干货!图解2016年上半年充电桩行业报告
“汇聚全产业链智慧协同创新” 2023电子峰会圆满落幕!
壹沓科技助力直播电商行业“数字化”转型,制胜双十二
小米Note2第一次现货开卖!双曲面+OLED售2799元
电压变化对无功补偿设备有影响吗
在不久的将来电动汽车将会取缔燃油车的地位
abb变频器最低频率设置
比Colab更方便的GPU平台-GPUlab
CAN协议数据帧的介绍
越来越多竞争者纷纷入局,工业物联网平台会走向垄断吗?
「含源码」关于NXP IMX8 Mini的图形开发指南(GPU)案例分享!
热参数如何与PCB温度或IC结温进行比较