【let'sdo|2026年第3期】过程贴:CubeMX2+CubeIDE2按键中断点灯(迎接变化)
https://forum.eepw.com.cn/thread/402317/1上一篇体验了全新的CubeMX2+CubeIDE2按键中断点灯。
本篇在此基础上使用按键启停步进电机。
步进电机:28BYJ-48
驱动电路:ULN2003芯片的驱动板


驱动板的硬件连接如上图。
1、2、3、4 接STM32的4个GPIO口
5接GND
6、7 接5V VCC
ABCDE接步进电机。
驱动方式也很简单:依次给各个相(1、2、3、4)输入高电平信号就可以了。注意,当给某一相输入信号的时候,其他相要重新置0。也就是说,同一时刻只能保持有一个相为高电平。
大概相当于如下:
while(1)
{
HAL_GPIO_WritePin(PA4_PORT, PA4_PIN, );
HAL_GPIO_WritePin(PB0_PORT, PB0_PIN, HAL_GPIO_PIN_RESET);
HAL_GPIO_WritePin(PC5_PORT, PC5_PIN, HAL_GPIO_PIN_RESET);
HAL_GPIO_WritePin(PC0_PORT, PC0_PIN, HAL_GPIO_PIN_RESET);
HAL_Delay(1);
HAL_GPIO_WritePin(PA4_PORT, PA4_PIN, HAL_GPIO_PIN_RESET);
HAL_GPIO_WritePin(PB0_PORT, PB0_PIN, );
HAL_GPIO_WritePin(PC5_PORT, PC5_PIN, HAL_GPIO_PIN_RESET);
HAL_GPIO_WritePin(PC0_PORT, PC0_PIN, HAL_GPIO_PIN_RESET);
HAL_Delay(1);
HAL_GPIO_WritePin(PA4_PORT, PA4_PIN, HAL_GPIO_PIN_RESET);
HAL_GPIO_WritePin(PB0_PORT, PB0_PIN, HAL_GPIO_PIN_RESET);
HAL_GPIO_WritePin(PC5_PORT, PC5_PIN, );
HAL_GPIO_WritePin(PC0_PORT, PC0_PIN, HAL_GPIO_PIN_RESET);
HAL_Delay(1);
HAL_GPIO_WritePin(PA4_PORT, PA4_PIN, HAL_GPIO_PIN_RESET);
HAL_GPIO_WritePin(PB0_PORT, PB0_PIN, HAL_GPIO_PIN_RESET);
HAL_GPIO_WritePin(PC5_PORT, PC5_PIN, HAL_GPIO_PIN_RESET);
HAL_GPIO_WritePin(PC0_PORT, PC0_PIN, );
HAL_Delay(1);
}整体main.c代码如下(包含了3轴加速度读取):
#include "main.h"
#include "string.h"
#include <stdint.h>
#include <stdio.h>
#include <math.h>
char msg1[] = "Hello World!\r\n";
char msg2[] = "\r\n";
// 扫描得到7bit=0x19,8bit地址0x32
#define LIS3DH_ADDR_8BIT (0x19 << 1)
#define LIS3DH_REG_WHO_AM_I 0x0F
#define LIS3DH_REG_CTRL1 0x20
#define LIS3DH_REG_CTRL4 0x23
#define LIS3DH_REG_OUT_XL 0x28
// 全局偏移变量
int16_t offset_x = 0;
int16_t offset_y = 0;
int16_t offset_z = 0;
uint8_t cal_done = 0;
#define CAL_SAMPLE_CNT 32 // 校准采样点数
volatile uint8_t ExtiDetected = 0; //USER按键中断检测
/**
* @brief 读取LIS3DH WHO_AM_I
*/
hal_status_t LIS3DH_ReadID(hal_i2c_handle_t *hi2c, uint8_t *id)
{
hal_status_t ret;
uint8_t reg = LIS3DH_REG_WHO_AM_I;
ret = HAL_I2C_MASTER_Transmit(hi2c, LIS3DH_ADDR_8BIT, ®, 1, 100);
if(ret != HAL_OK) return ret;
ret = HAL_I2C_MASTER_Receive(hi2c, LIS3DH_ADDR_8BIT, id, 1, 100);
return ret;
}
/**
* @brief LIS3DH初始化:100Hz,XYZ使能,±2g高分辨率
*/
hal_status_t LIS3DH_Init(hal_i2c_handle_t *hi2c)
{
hal_status_t ret;
uint8_t buf[2];
// CTRL1: ODR=100Hz, X Y Z enable --> 0x57
buf[0] = LIS3DH_REG_CTRL1;
buf[1] = 0x57;
ret = HAL_I2C_MASTER_Transmit(hi2c, LIS3DH_ADDR_8BIT, buf, 2, 100);
if(ret != HAL_OK) return ret;
// CTRL4: ±2g, HR=1 --> 0x08
buf[0] = LIS3DH_REG_CTRL4;
buf[1] = 0x08;
ret = HAL_I2C_MASTER_Transmit(hi2c, LIS3DH_ADDR_8BIT, buf, 2, 100);
return ret;
}
/**
* @brief 连续读取XYZ原始数据 6字节
* @param raw_buf[6] XL,XH,YL,YH,ZL,ZH
*/
hal_status_t LIS3DH_ReadRawXYZ(hal_i2c_handle_t *hi2c, uint8_t *raw_buf)
{
hal_status_t ret;
// bit7=1 自动增量
uint8_t reg = 0x80 | LIS3DH_REG_OUT_XL;
ret = HAL_I2C_MASTER_Transmit(hi2c, LIS3DH_ADDR_8BIT, ®, 1, 100);
if(ret != HAL_OK) return ret;
ret = HAL_I2C_MASTER_Receive(hi2c, LIS3DH_ADDR_8BIT, raw_buf, 6, 100);
return ret;
}
/**
* @brief 打印ID
*/
void LIS3DH_PrintID_UART2(void)
{
hal_status_t ret;
uint8_t chip_id = 0;
char msg[64];
ret = LIS3DH_ReadID(mx_i2c1_i2c_gethandle(), &chip_id);
if(ret == HAL_OK)
{
snprintf(msg, sizeof(msg), "LIS3DH ID: 0x%02X, expect 0x33\r\n", chip_id);
}
else
{
snprintf(msg, sizeof(msg), "LIS3DH I2C read FAIL");
}
HAL_UART_Transmit(mx_usart2_uart_gethandle(), (uint8_t *)msg, strlen(msg), 100);
}
/**
* @brief 读取XYZ并串口打印(mg单位)
*/
void LIS3DH_PrintXYZ_UART2(void)
{
hal_status_t ret;
uint8_t raw[6];
int16_t x_raw, y_raw, z_raw;
int16_t x, y, z;
char msg[128];
ret = LIS3DH_ReadRawXYZ(mx_i2c1_i2c_gethandle(), raw);
if(ret != HAL_OK)
{
snprintf(msg, sizeof(msg), "LIS3DH XYZ read error\r\n");
}
else
{
x_raw = (int16_t)((raw[1] << 8) | raw[0]);
y_raw = (int16_t)((raw[3] << 8) | raw[2]);
z_raw = (int16_t)((raw[5] << 8) | raw[4]);
// HR高分辨率模式:12bit,左对齐,必须右移4位!
x = x_raw >> 4;
y = y_raw >> 4;
z = z_raw >> 4;
// ±2g:1 LSB = 1mg
snprintf(msg, sizeof(msg), "ACC: X=%d mg, Y=%d mg, Z=%d mg\r\n", x, y, z);
}
HAL_UART_Transmit(mx_usart2_uart_gethandle(), (uint8_t *)msg, strlen(msg), 100);
}
/**
* @brief 零点校准函数,调用前保持传感器水平平放
*/
void LIS3DH_CalibrateZero(void)
{
uint8_t raw[6];
int16_t x_raw, y_raw, z_raw;
int32_t sum_x = 0, sum_y = 0, sum_z = 0;
for(uint16_t i = 0; i < CAL_SAMPLE_CNT; i++)
{
// 等待读取成功
while(LIS3DH_ReadRawXYZ(mx_i2c1_i2c_gethandle(), raw) != HAL_OK);
x_raw = (int16_t)((raw[1] << 8) | raw[0]);
y_raw = (int16_t)((raw[3] << 8) | raw[2]);
z_raw = (int16_t)((raw[5] << 8) | raw[4]);
int16_t x = x_raw >> 4;
int16_t y = y_raw >> 4;
int16_t z = z_raw >> 4;
sum_x += x;
sum_y += y;
sum_z += z;
HAL_Delay(20);
}
// 求平均偏移;Z理想值是1000mg
offset_x = sum_x / CAL_SAMPLE_CNT;
offset_y = sum_y / CAL_SAMPLE_CNT;
offset_z = sum_z / CAL_SAMPLE_CNT - 1000;
cal_done = 1;
}
/**
* @brief 读取并打印【校准后】XYZ,单位 mg
*/
void LIS3DH_PrintXYZ_Cal(void)
{
hal_status_t ret;
uint8_t raw[6];
int16_t x_raw, y_raw, z_raw;
int16_t x, y, z;
char msg[128];
ret = LIS3DH_ReadRawXYZ(mx_i2c1_i2c_gethandle(), raw);
if(ret != HAL_OK)
{
snprintf(msg, sizeof(msg), "LIS3DH XYZ read error, ret=%u\r\n", ret);
}
else
{
x_raw = (int16_t)((raw[1] << 8) | raw[0]);
y_raw = (int16_t)((raw[3] << 8) | raw[2]);
z_raw = (int16_t)((raw[5] << 8) | raw[4]);
// 减去偏移,得到校准值
x = (x_raw >> 4) - offset_x;
y = (y_raw >> 4) - offset_y;
z = (z_raw >> 4) - offset_z;
//snprintf(msg, sizeof(msg), "ACC: X=%d mg, Y=%d mg, Z=%d mg\r\n", x, y, z);
snprintf(msg, sizeof(msg), "%d,%d,%d,", x, y, z);
}
HAL_UART_Transmit(mx_usart2_uart_gethandle(), (uint8_t *)msg, strlen(msg), 100);
}
int main(void)
{
if (mx_system_init() != SYSTEM_OK)
{
return (-1);
}
else
{
LIS3DH_PrintID_UART2();
LIS3DH_Init(mx_i2c1_i2c_gethandle());
// 上电校准!【板子平放,不要晃动!】
LIS3DH_CalibrateZero();
while (1) {
//HAL_GPIO_TogglePin(PA5_PORT, PA5_PIN);
//HAL_UART_Transmit(mx_usart2_uart_gethandle(), msg1, strlen(msg1), 100);
for(int i=0;i<32;i++) //每个数据块包含63帧加速度数据
{
if(i<31)
LIS3DH_PrintXYZ_Cal(); // 读取并发送一帧3轴加速度数据
else //最后一帧回车
HAL_UART_Transmit(mx_usart2_uart_gethandle(), (uint8_t *)msg2, strlen(msg2), 100);
}
//HAL_Delay(500);
if(ExtiDetected == 1)
{
HAL_GPIO_WritePin(PA4_PORT, PA4_PIN, HAL_GPIO_PIN_SET);
HAL_GPIO_WritePin(PB0_PORT, PB0_PIN, HAL_GPIO_PIN_RESET);
HAL_GPIO_WritePin(PC5_PORT, PC5_PIN, HAL_GPIO_PIN_RESET);
HAL_GPIO_WritePin(PC0_PORT, PC0_PIN, HAL_GPIO_PIN_RESET);
HAL_Delay(1);
HAL_GPIO_WritePin(PA4_PORT, PA4_PIN, HAL_GPIO_PIN_RESET);
HAL_GPIO_WritePin(PB0_PORT, PB0_PIN, HAL_GPIO_PIN_SET);
HAL_GPIO_WritePin(PC5_PORT, PC5_PIN, HAL_GPIO_PIN_RESET);
HAL_GPIO_WritePin(PC0_PORT, PC0_PIN, HAL_GPIO_PIN_RESET);
HAL_Delay(1);
HAL_GPIO_WritePin(PA4_PORT, PA4_PIN, HAL_GPIO_PIN_RESET);
HAL_GPIO_WritePin(PB0_PORT, PB0_PIN, HAL_GPIO_PIN_RESET);
HAL_GPIO_WritePin(PC5_PORT, PC5_PIN, HAL_GPIO_PIN_SET);
HAL_GPIO_WritePin(PC0_PORT, PC0_PIN, HAL_GPIO_PIN_RESET);
HAL_Delay(1);
HAL_GPIO_WritePin(PA4_PORT, PA4_PIN, HAL_GPIO_PIN_RESET);
HAL_GPIO_WritePin(PB0_PORT, PB0_PIN, HAL_GPIO_PIN_RESET);
HAL_GPIO_WritePin(PC5_PORT, PC5_PIN, HAL_GPIO_PIN_RESET);
HAL_GPIO_WritePin(PC0_PORT, PC0_PIN, HAL_GPIO_PIN_SET);
HAL_Delay(1);
}
else
{
HAL_GPIO_WritePin(PA4_PORT, PA4_PIN, HAL_GPIO_PIN_RESET);
HAL_GPIO_WritePin(PB0_PORT, PB0_PIN, HAL_GPIO_PIN_RESET);
HAL_GPIO_WritePin(PC5_PORT, PC5_PIN, HAL_GPIO_PIN_RESET);
HAL_GPIO_WritePin(PC0_PORT, PC0_PIN, HAL_GPIO_PIN_RESET);
HAL_Delay(5);
}
}
}
}
//中断回调函数
void HAL_EXTI_TriggerCallback(hal_exti_handle_t *hexti, hal_exti_trigger_t trigger)
{
/* Prevent unused argument compilation warning */
STM32_UNUSED(trigger);
if (hexti == mx_gpio_default_exti13_gethandle())
{
//ExtiDetected = 1;
ExtiDetected ^= 1; 按下按键,状态翻转
}
}运行效果:
按下USER按钮:电机转动,
再次按下USER按钮:电机停止,
如此反复。

我要赚赏金
