开发流程:

搞清原理,知道用哪个引脚

到cubemx配置引脚并更新keil

在对应层(common interface等文件夹)中创建.c .h文件

在keil中添加层文件夹路径并将文件纳入编译范围内

在vscode中开发

先在原理图中搜索数据手册弄清驱动原理,包括引脚定义、通讯协议

从高层项底层写

I 搞清原理,知道用哪个引脚

产品简介

智能计步传感器 ds3553 集成了一个高精度、高分辨率的明皜加速度传感器,和一个 8 位低功耗嵌入式微控器的专用架构。由于其低功耗、高精度的性能优势,被广泛的应用于各种智能穿戴式计步产品,包括手环、计步鞋、计步器、运动耳机等。

可以看到,芯片有五路控制引脚,分别是I2C的SDA、一个I2C的时钟线、两个中断INT、一个通讯片选,查阅手册得知:

通讯协议——I2C

使用PB6 PB7引脚,可以选用普通 高速,分别是100k 400kHz,我们使用100kHz即可。

I2C的通讯前提是知晓对方地址,数据手册给出I2C地址是0100111X,也就是27X。设备可以收发,收的时候读写位是1,整体地址是01001111,也就是4F;写的时候是4E。

在收发信号的时候通常有两种实现形式,一种是软件模拟,另一种是硬件实现,这取决于MCU中是否有专门处理收发的设备,我们看到STM32的逻辑图,是专门有处理I2C的硬件的,那么我们只需要调用API就能实现收发了,而不需要手动模拟拉高拉低信号线和时钟线了。

I2C的读写操作

芯片的I2C和常规I2C很类似,只是发送设备地址的地方替换为了写命令。

流程就是主控发送开始信号后,发送要写入的信号,从机收到后回复。回复后主机发送要写入的地址,从机收到后相应。接着就是发送一个字节响应一个字节,最后由主控停止。

读也一样,可以理解为”真读假写“。这里也是主控发送开始,并发送写指令,这个写指令是为了告诉从机要读取的寄存器的地址。从机收到后回复,然后主控再发送读指令,从机响应后直接开始发送数据,发送一个字节主控回应一个字节,最后主控停止。

工作模式

不用管,从正常模式切换到休眠模式是自动的,待机模式不使用,本身电流就不大。

寄存器

有四个只读,一个读写寄存器。

STEP分别存储步数的低中高八位。
USER_SET用于存储设备状态,可以被修改。
CHIPID是不可改的地址,用于验证,如果读取到13就说明是正常的。

II 到cubemx配置引脚并更新keil

我们记住一个规律:

1. 通用数字输出引脚 → 通常使用「推挽输出」(Push-Pull)
2. 通用数字输入引脚 → 若外部电路未提供确定电平,则应配置「上拉」或「下拉」

配置I2C通讯引脚

根据引脚定义,我们先配置I2C通讯引脚,自动匹配到PB6 7上。

由于我们决定使用100kHz的普通模式,因此不要切换到400k的高速模式。

在下面的这些内容是作为从机才会使用的:

比如我们开发计步芯片,这个计步芯片如果用stm32,那么就作为从机了,此时就需要配置从机地址等信息了

配置中断引脚(输入用上下拉)

我们就仅配置中断1,也就是计步脉冲。对那个PA12引脚,选择这个EXTI(external Interrupt)外部中断。

然后看到引脚定义,中断是通过低电平触发的,因此一般状态应该是高电平,也就是说,需要拉一个上拉电阻,当中断时接收到低电平判断。

在GPIO页面找到PA12 然后mode改为下降沿falling edge检测,然后拉一个pullup电阻。

配置片选引脚(输出用推挽输出)

低电平选中,因此一般状态应该是高电平。采用推挽输出。

点击generatecode。

III 在对应层(common interface等文件夹)中创建.c .h文件

IV 在keil中添加层文件夹路径并将文件纳入编译范围内

出现这两个就是正常了

V 在vscode中开发

总体思路:

先在原理图中搜索数据手册弄清驱动原理 从高层项底层写

开发流程:

在.c文件include自己的头文件,然后在.h文件先#ifndef def endif

然后在.h定义暴露给外部的函数,并/** */写函数声明和参数声明

复制下来到.c文件中写实现函数,如果需要嵌套函数,则需要static声明

在main.c中的USERINCLUDE加入头文件,并在USERCODE2加入测试代码

在.h中定义暴露给外部的函数:

由于计步芯片有较多寄存器,因此需要提前配置user_set寄存器:

声明一个初始化函数:

声明一个获取步数的函数:

由于返回值有高中低各8位,因此最近的数据类型是32位

#ifndef __INT_STEP_H__
#define __INT_STEP_H__

#include "i2c.h"

/**
 * @brief 初始化ds3553芯片 设置user_set寄存器
 * 
 */
void Int_step_init(void);

/**
 * @brief 读取ds3553芯片的step数值
 * 
 * @return uint32_t step数值
 */
uint32_t Int_step_get_count(void);

#endif /* __INT_STEP_H__ */

复制下来到.c文件中写实现函数,如果需要嵌套函数,则需要static声明

初始化函数要初始化所有寄存器的内容

因此先宏定义在.h中

#define DS3553_I2C_ADDR_W 0x4E //ds3553芯片写入地址
#define DS3553_I2C_ADDR_R 0x4F //ds3553芯片读取地址
#define DS3553_CHIP_ID 0x01
#define DS3553_USER_SET 0xC3
#define DS3553_CNT_L 0xC4
#define DS3553_CNT_M 0xC5
#define DS3553_CNT_H 0xC6

然后由于我们使用硬件控制的I2C,因此可以直接调用HAL库的I2C通讯函数:

这个函数有七个参数,分别是:

1) i2c编号
2) 目标设备读写地址
3) 寄存器地址
4) 寄存器长度(bit)
5)写入数据指针
6) 写入数据长度(byte)
7) 超时时间

'''1) i2c编号 2) 目标设备读写地址 3) 寄存器地址 4) 寄存器长度(bit) 5)写入数据指针 6) 写入数据长度(byte) 7) 超时时间'''
    HAL_I2C_Mem_Write(&hi2c1, DS3553_I2C_ADDR_W, 0x00, 1, &user_set, 1, 1000);

配置user_set寄存器 正常模式:关闭抬手打断 启用计步中断 使用计步器算法 开启去噪 正常灵敏度

因此是0x1A:  0 0 0 1 1 0 10

将其作为data通过I2C传给DS3553,使用这个HAL函数:

/**
 * @brief 初始化ds3553芯片 设置user_set寄存器
 * 
 */
 void Int_step_init(void){
    //配置user_set寄存器 正常模式:关闭抬手打断 启用计步中断 使用计步器算法 开启去噪 正常灵敏度
    uint8_t data = 0x1A;
    //使用I2C寄存器的写入函数
    //1) i2c编号 2) 目标设备读写地址 3) 寄存器地址 4) 寄存器长度(bit) 5)写入数据指针 6) 写入数据长度(byte) 7) 超时时间
    HAL_I2C_Mem_Write(&hi2c1, DS3553_I2C_ADDR_W, DS3553_USER_SET, I2C_MEMADD_SIZE_8BIT, &data, 1, 1000);
 }

在读取之前可以验证一下I2C通讯是否成功建立,可以尝试读取DS3553的从设备地址(不可改)

这个读取的函数和写入的函数基本一致:

void Int_step_init(void){
    uint8_t CHIP_ID=0;
    HAL_I2C_Mem_Read(&hi2c1, DS3553_I2C_ADDR_R, DS3553_CHIP_ID, I2C_MEMADD_SIZE_8BIT, &CHIP_ID, 1, 1000);
    debug_printf("DS3553 CHIP ID:%x\r\n",CHIP_ID);
    //配置user_set寄存器 正常模式:关闭抬手打断 启用计步中断 使用计步器算法 开启去噪 正常灵敏度
    uint8_t data = 0x1A;
    //使用I2C寄存器的写入函数
    //1) i2c编号 2) 目标设备读写地址 3) 寄存器地址 4) 寄存器长度(bit) 5)写入数据指针 6) 写入数据长度(byte) 7) 超时时间
    HAL_I2C_Mem_Write(&hi2c1, DS3553_I2C_ADDR_W, DS3553_USER_SET, I2C_MEMADD_SIZE_8BIT, &data, 1, 1000);
 }

目前仍然无法实现I2C通讯,因为并不满足I2C通讯条件:

我们还需要拉低片选线,并使用延时:

这个DS3553_CS_GPIO_Port和DS3553_CS_Pin的宏定义是点击GenerateCode自动生成的,可以在main.h中查找到相关定义。

void Int_step_init(void){
    uint8_t CHIP_ID=0;
    HAL_I2C_Mem_Read(&hi2c1, DS3553_I2C_ADDR_R, DS3553_CHIP_ID, I2C_MEMADD_SIZE_8BIT, &CHIP_ID, 1, 1000);
    debug_printf("DS3553 CHIP ID:%x\r\n",CHIP_ID);

    HAL_GPIO_WritePin(DS3553_CS_GPIO_Port, DS3553_CS_Pin, GPIO_PIN_RESET); //使能DS3553芯片
    HAL_Delay(5); //延时5ms
    //配置user_set寄存器 正常模式:关闭抬手打断 启用计步中断 使用计步器算法 开启去噪 正常灵敏度
    uint8_t data = 0x1A;
    //使用I2C寄存器的写入函数
    //1) i2c编号 2) 目标设备读写地址 3) 寄存器地址 4) 寄存器长度(bit) 5)写入数据指针 6) 写入数据长度(byte) 7) 超时时间
    HAL_I2C_Mem_Write(&hi2c1, DS3553_I2C_ADDR_W, DS3553_USER_SET, I2C_MEMADD_SIZE_8BIT, &data, 1, 1000);
    HAL_GPIO_WritePin(DS3553_CS_GPIO_Port, DS3553_CS_Pin, GPIO_PIN_SET); //关闭DS3553芯片
    HAL_Delay(10); //延时10ms
   }

设置全局变量存储步数,一共24位

在读取函数中,首先启动芯片,拉低引脚,然后读取然后拉高,然后拼接每次读取的一字节内容。

#include "Int_step.h"
#include "Com_debug.h"

//记录步数的变量
uint8_t step_cnt[3]={0};
/**
 * @brief 初始化ds3553芯片 设置user_set寄存器
 * 
 */
 void Int_step_init(void){
    uint8_t CHIP_ID=0;
    HAL_I2C_Mem_Read(&hi2c1, DS3553_I2C_ADDR_R, DS3553_CHIP_ID, I2C_MEMADD_SIZE_8BIT, &CHIP_ID, 1, 1000);
    debug_printf("DS3553 CHIP ID:%x\r\n",CHIP_ID);

    HAL_GPIO_WritePin(DS3553_CS_GPIO_Port, DS3553_CS_Pin, GPIO_PIN_RESET); //使能DS3553芯片
    HAL_Delay(5); //延时5ms
    //配置user_set寄存器 正常模式:关闭抬手打断 启用计步中断 使用计步器算法 开启去噪 正常灵敏度
    uint8_t data = 0x1A;
    //使用I2C寄存器的写入函数
    //1) i2c编号 2) 目标设备读写地址 3) 寄存器地址 4) 寄存器长度(bit) 5)写入数据指针 6) 写入数据长度(byte) 7) 超时时间
    HAL_I2C_Mem_Write(&hi2c1, DS3553_I2C_ADDR_W, DS3553_USER_SET, I2C_MEMADD_SIZE_8BIT, &data, 1, 1000);
    HAL_GPIO_WritePin(DS3553_CS_GPIO_Port, DS3553_CS_Pin, GPIO_PIN_SET); //关闭DS3553芯片
    HAL_Delay(10); //延时10ms
   }

 /**
  * @brief 读取ds3553芯片的step数值
  * 
  * @return uint32_t step数值
  */
 uint32_t Int_step_get_count(void){
    //打开片选引脚
    HAL_GPIO_WritePin(DS3553_CS_GPIO_Port, DS3553_CS_Pin, GPIO_PIN_RESET); //使能DS3553芯片
    HAL_Delay(5); //延时5ms
    //读取step数值
    HAL_I2C_Mem_Read(&hi2c1, DS3553_I2C_ADDR_R, DS3553_CNT_L, I2C_MEMADD_SIZE_8BIT, step_cnt, 3, 1000);//要从地址最低的往后读3字节,因此要用CNT_L
    HAL_Delay(10); //延时10ms
    HAL_GPIO_WritePin(DS3553_CS_GPIO_Port, DS3553_CS_Pin, GPIO_PIN_SET); //关闭DS3553芯片
    return (step_cnt[0] | (step_cnt[1]<<8) | (step_cnt[2]<<16));
   }

在main.c中的USERINCLUDE加入头文件,并在USERCODE2加入测试代码

注意,由于这个输入是持续获取的,因此要放在while中,而不是USERCODE2中,这和Arduino框架很类似,前面的代码是setup(),这里循环执行的代码会写在loop()中。

while (1)
  {
    /* USER CODE END WHILE */
    uint32_t step_count = Int_step_get_count();
    //printf("step count: %d\r\n", step_count);
    debug_printf("step count: %d\r\n", step_count);
    HAL_Delay(1000);
    /* USER CODE BEGIN 3 */
  }

正常编译。

打印出来是19,这是十进制下的0x13。

输出正常:

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐