使用 STM32CubeMX 创建一个 STM32F103C8T6 Makefile 工程, 添加一些基本文件, 例如 .clang-format, .gitignore, clang-format.py, build.sh 文件
- .clang-format 源码文件统一格式化规范
- build.sh 辅助编译脚本, 一键编译下载, openocd 下载调试
- .gitignore 版本管理文件
- clang-format.py 批量格式化源码脚本文件
| IO 名称 | 说明 |
|---|---|
| PA9 | USART_TX |
| PA10 | USART_RX |
STM32CubeMX 的默认配置, 波特率: 115200, 数据位长度: 8, 无校验
/* ANCHOR - gcc printf 重定义 */
# if3 /* printf 重定义 */
# 4 ifdef __GNUC__
# 5 define PUTCHAR_PROTOTYPE int __io_putchar(int ch)
# 6 else
# 7 define PUTCHAR_PROTOTYPE int fputc(int ch, FILE* f)
# 8 endif /* __GNUC__ */
PUTCHAR_PROTOTYPE
{
/* 实现串口发送一个字节数据的函数 */
while ((USART1->SR & 0x40) == 0)
{
}
USART1->DR = (uint8_t)ch;
return ch;
}
int _write(int file, char* ptr, int len)
{
int DataIdx;
for (DataIdx = 0; DataIdx < len; DataIdx++)
{
__io_putchar(*ptr++);
}
return len;
}
# 9 endif /* printf 重定义 */注意: 上面的串口 printf 重定义是针对 GCC 编译器的, 并且上述的串口 printf 重定义无法输出浮点数, 为了输出浮点数还需要设置一个链接选项
打开 Makefile 文件, 然后做出以下修改:
LDFLAGS = $(MCU) -specs=nano.specs -T $(LDSCRIPT) $ (LIBDIR) $(LIBS) -Wl,-Map=$(BUILD_DIR)/$(TARGET).map,--cref -Wl,--gc-sections
--> 在链接选项中增加 -u _printf_float, 添加后编译生成的文件增大 8KB
LDFLAGS = $(MCU) -specs=nano.specs -u _printf_float -T $(LDSCRIPT) $ (LIBDIR) $(LIBS) -Wl,-Map=$(BUILD_DIR)/$(TARGET).map,--cref -Wl,--gc-sections原理图分析:
- 当 PA4 输出低电平的时候, LED 亮
- 当 PA4 输出高电平的时候, LED 灭
- 让 PA4 的初始状态为高电平
| IO 口 | 说明 |
|---|---|
| PA4 | LED |
推挽输出, 默认高电平 (LED 灭), 速率低
原理图分析:
- 按键按下, PA5 为低电平
- 按键释放, PA5 为高电平
| IO 口 | 说明 |
|---|---|
| PA5 | 用户按键 |
浮空输入, 由于外部有上拉电阻存在, 所以默认 PA5 是高电平
为了不让按键影响其他任务的执行, 需要使用非阻塞的方式进行按键扫描
非阻塞有三种实现方式:
- while(1) 循环作为时基 (受任务数量, 时基不确定)
- 定时器作为时基, 使用周期性定时器中断来检测按键 (时基确定)
- 按键外部中断触发, 定时器辅助
按键扫描有2种判断方式:
- 状态机 (逻辑清晰)
- 条件判断+标志位 (逻辑复杂, 需要很多条件判断和标志位变量)
选择软件定时器+状态机的组合: 详情请见源码
- 2cm-450cm 测量范围, 超过或小于这个测量范围, 测量误差会变大
Trig 端发一个 10US 以上的高电平, 就可以在 Echo 口等待高电平输出, 一有输出就可以开定时器计时, 当此口变为低电平时就可以读定时器的值, 此时就为此次测距的时间, 方可算出距离, 如此不断的周期测, 即可以达到你移动测量的值
方式 1: 出现上升沿, 开启定时器从 0 开始计数, 然后 while(HAL_GPIO_ReadPin(hc_sr04_device -> echo_port, hc_sr04_device -> echo_pin) == GPIO_PIN_SET); 等待回响信号的下降沿, 停止计数, 根据定时器的计数值来计算距离
方式 2: 使用定时器的输入捕获功能, 在捕获到上升沿或下降沿时的中断函数中记录下定时器的计数值, 并计算距离
方法 1, 由于用了 while 等待回响信号的高电平结束, 会造成阻塞, 有时候不太好用
使用非阻塞方式需要注意中断嵌套对测量的影响, 如果一个中断 A 的优先级高于输入捕获和定时器更新中断, 并且中断 A 周期性产生, 那么这个中断 A 就会影响超声波测距
原理图分析:
PB1 连接 Trig 引脚, PB0 连接 Echo 引脚
PB1 连接 Trig 引脚, 主要作用是触发 HC-SR04, 所以配置为推挽输出, 默认输出低电平
PB0 连接 Echo 引脚, 主要作用是接收 HC-SR04 发过来的高电平脉宽, 需要测量脉宽, 配置为定时器输入捕获
使用 OLED 128*64 像素, 驱动芯片: SSD1306, 通信方式: IIC, SPI (4 线, 3 线), 8080, 6080
选择 SPI 3线 通信
GND ----------------- GND
VCC ----------------- 3.3V
SCL ----------------- PB5
SDA ----------------- PB4
RES ----------------- PB3 (RST/RES: 硬复位 OLED, 低电平有效)
DC ----------------- PA15 (数据/命令标志 0: 命令, 1: 数据)
使用 MPU6050 检测数据, 计算欧拉角, 获得小车姿态
MPU6050 与 MCU 是通过 IIC 进行通信, 可以通过 GPIO 模拟 IIC, 也可以直接使用 MCU 的 IIC 硬件电路
需要注意 MPU6050 芯片放置方向, 最好让小车前进方向与 x 轴平行
最开始是跟着6.4.MPU6050(下)_哔哩哔哩_bilibili学习, 但是测量的数据并不准确, 从网上查找 MPU6050 校准, 大多是使用 eMPL 库提供的校准功能
参考: STM32 HAL库移植MPU6050 DMP库(跟着做就能成功)-CSDN博客
| IO 口 | 说明 |
|---|---|
| PA12 | MPU6050 的 INT 中断引脚 |
| PB8, PB9 | IIC 的 SCL 和 SDA |
在 STM32F103C8 有 10 个外部通道、12 位 ADC, 输入范围为 0V -3.3V, 它将 0 到 3.3 伏之间的输入电压映射为 0 到 4095 之间 (
通过电阻将 VIN 分压, VOUT 进入 ADC 采集通道
$V_{OUT}=\frac{V_{IN}1}{11} \to V_{IN}=11V_{OUT}$ ,
PA6 配置为 ADC1_CH6
通过编码器获得电机转速数据
左轮编码器分配两个 GPIO 口, PA0 和 PA1 , 分别配置为 TIM2_CH1 和 TIM2_CH2
右轮编码器分配两个 GPIO 口, PB6 和 PB7, 分别配置为 TIM4_CH1 和 TIM4_CH2
为了后续远程调试和远程控制, 需要借助蓝牙模块
当然也可以本地调试, 缺点: 每次调试都需要重新下载, 编译, 比较麻烦
PB10 和 PB11, 分别配置为 USART3_TX, USART3_RX
MCU 的串口 1 与计算机连接, 为了让与 HC-05 连接的蓝牙模块也能直接与 PC 串口软件通信, 需要做以下操作:
-
PC 通过串口 1 发送数据到 MCU, MCU 接收到数据后, 通过串口 3 发送给 HC-05
- 打开串口 1 的接收中断, 在接收中断处理程序中, 将数据通过 串口 3 发送给 HC-05
-
HC-05 通过串口 3 发送数据给 MCU, MCU 接收数据后, 通过串口 1 发送给 PC 串口软件
- 打开串口 3 的接收中断, 在接收中断处理程序中, 将数据通过 串口 1 发送给 PC






