具身智能第6周倾角报警器
STM32F407 + MPU6500 倾角报警器:从原始六轴数据到 45° 超限报警的完整实现
前言:上一版只把加速度、角速度原样甩到 LCD 上,看数还得自己脑补角度。这一版做了升级——把原始数据换成倾角和姿态角,再加上 45° 超限报警:一旦倾斜越过阈值,屏幕底部状态行立刻由绿色
NORMAL翻成红色WARNING,回正后自动恢复。看起来只是多算了几行公式,但真正动手写才发现,坐标系、正负号、抖动、残影,每个环节都能卡你半天。下面把整个实现过程和实测数据完整记录一下。
一、效果先看
上电后屏幕分成几块内容,一屏就能把传感器的所有关键信息看全:
26 ZHI NENG 1 BAN-DI 5 ZU
LCD ID: 33918 IMU OK
AX: 0.1 AY: 0.1 AZ: 10.3
GX: 5.9 GY: -0.5 GZ: -19.1
TILT ANGLE DEG
TX: 0.7 TY: 0.6 TZ: 0.9
ATTITUDE DEG
PITCH: -0.7 ROLL: 0.6 YAW: -81.4
STATUS: NORMAL
LIMIT 45 DEG
板子水平静置时,AZ ≈ 10.3 m/s²(接近 9.8,略有零偏),AX/AY 基本为 0,三个倾角都在 0.5° 上下浮动,PITCH/ROLL 在 ±1° 内,状态是绿色的 NORMAL。
视频里手动翻转板子后,状态行会立刻变成红色 WARNING
二、硬件与环境
| 硬件 / 软件 | 说明 |
|---|---|
| 主控板 | STM32F407ZGT6(正点原子探索者) |
| 显示屏 | 4.3 寸 ALIENTEK TFTLCD,竖屏,读取到的 LCD ID 为 33918 |
| 传感器 | MPU6500 六轴 IMU 模块外挂,4 线 I2C 接入(VCC/GND/SCL/SDA) |
| 开发环境 | STM32CubeIDE + HAL 库 |
| 刷新率 | 10Hz(主循环 HAL_Delay(100)) |
和上一版最大的区别是:传感器不再靠板载资源,而是从一块独立的 MPU 模组用杜邦线接进 I2C,模组板上丝印标着 X/Y/Z 三个轴向——这块小板的正放方向决定了你后面所有角度值的正负号,建议第一步就先在桌上摆正、记下坐标轴朝向再动手写换算。
三、整体思路
数据链路可以概括成一条流水线:
MPU6500 原始数据 (ax ay az / gx gy gz)
│
├─ 加速度 → atan2f 换算 → 倾角 TX/TY/TZ
│
├─ 加速度 + 陀螺仪 → 互补滤波 → 姿态角 PITCH/ROLL/YAW
│
└─ |PITCH|、|ROLL| 与 45° 比较 → NORMAL / WARNING
│
└─ LCD 分区刷新(黑底彩色文字,带残影清除)
这样分层的好处是:原始数据永远保留在最上面两行,方便排错;中间两层是"加工结果";最底下的状态行只负责报警决策,逻辑互不干扰。
四、界面布局:先把坐标算清楚
4.3 寸屏是 800×480 的竖屏,文字一行大约占 20~30 像素。我的做法是固定行距、分区块绘制:
| Y 坐标 | 内容 | 颜色 |
|---|---|---|
| 20 | 标题(团队标识) | 白色 |
| 40 | LCD ID + IMU OK | 白 / 绿 |
| 80 ~ 140 | AX / AY / AZ(m/s²) | 白标签 + 青色数值 |
| 170 ~ 230 | GX / GY / GZ(deg/s) | 白标签 + 品红数值 |
| 260 | TILT ANGLE DEG + TX/TY/TZ | 黄色标题 |
| 320 | ATTITUDE DEG + PITCH/ROLL/YAW | 黄色标题 |
| 380 | STATUS: NORMAL / WARNING | 绿 / 红 |
| 405 | LIMIT 45 DEG | 白色 |
标签和数值分成两次绘制(LCD_ShowString 画标签、LCD_ShowFloat 画数字),好处是数字位宽变化时不会把标签一起重画,闪烁更少。
五、核心代码解析
1. 从加速度到倾角
加速度计静止时,测得的就是重力在三个轴上的投影。所以某个轴的倾角可以直接用 atan2f 求:
/* 倾角换算:atan2f 比 asin 更稳,能全角度覆盖且不怕除零 */
#define RAD2DEG 57.29578f
s.tx = atan2f(s.ay, s.az) * RAD2DEG; /* 绕 X 轴倾角 */
s.ty = atan2f(s.ax, s.az) * RAD2DEG; /* 绕 Y 轴倾角 */
s.tz = 0.0f; /* Z 轴靠重力无法解算,交给陀螺仪 */
这里用 atan2f(y, x) 而不是 asin(),一是避免 az 接近 0 时的除零问题,二是 ±180° 范围内都有解、不会在象限边界跳变。静止实测 TX/TY 都是 0.6~0.7°,说明零偏很小,没有再额外做校准。
2. 姿态角:互补滤波
只用加速度算出来的角度有个致命缺点——它把运动加速度也当成了重力,板子一晃,角度就乱跳。所以要把陀螺仪积分的结果按权重融合进来:
/* 加速度解算的绝对角度(低频准,高频抖) */
float pitch_acc = -atan2f(s.ay, sqrtf(s.ax * s.ax + s.az * s.az)) * RAD2DEG;
float roll_acc = atan2f(s.ax, s.az) * RAD2DEG;
/* 陀螺仪积分(高频准,低频漂) */
pitch = K * (pitch + s.gy * dt) + (1 - K) * pitch_acc; /* K 取 0.95 左右 */
roll = K * (roll + s.gx * dt) + (1 - K) * roll_acc;
yaw += s.gz * dt; /* 偏航无重力参考,只能靠陀螺仪积分,会缓慢漂移 */
K 越大越信陀螺仪、越平滑但越容易漂;越小越信加速度计、静态准但动态抖。实测取 0.95 左右,静态时 PITCH/ROLL 稳定在 ±1°,翻转过程也跟得上手速。YAW 因为没有磁力计做参考,只能纯积分,长时间会漂——这也是演示中同一个朝向 YAW 数值会慢慢变的原因。
3. 45° 报警判据与回差
报警判断只看两个姿态角,取绝对值,任一轴超限就报警:
/* 倾角超限判断:PITCH/ROLL 任一超过 45° 即报警 */
float tilt = fmaxf(fabsf(s.pitch), fabsf(s.roll));
static uint8_t warn = 0;
if (!warn && tilt > 45.5f) warn = 1; /* 超 45.5° 进入报警 */
else if (warn && tilt < 43.5f) warn = 0; /* 回落到 43.5° 以下才解除 */
LCD_ShowString(20, 380, warn ? "STATUS: WARNING" : "STATUS: NORMAL ",
warn ? LCD_RED : LCD_GREEN, LCD_BLACK);
两个细节值得说:
- 取绝对值:向左倾和向右倾都是危险姿态,不能只看正方向;
- 2° 回差(迟滞):如果只用
>45单向判断,角度在 45° 附近来回抖动时状态行会疯狂闪红绿。加上 45.5/43.5 的死区后,状态切换就非常干净。
实测数据刚好卡在边界上,也验证了这个判据:
| 场景 | PITCH | ROLL | 状态显示 |
|---|---|---|---|
| 水平静置 | -0.7 | 0.6 | NORMAL |
| 倾斜 44.7°(临界) | -44.7 | -1.0 | NORMAL |
| 倾斜 46.5°(越界) | -8.5 | -46.5 | WARNING |
| 大角度翻转 | -52.0 | -29.6 | WARNING |
-44.7° 不报警、-46.5° 报警,边界行为和设计一致。
4. 残影:状态行文字长度不一致的坑
"STATUS: WARNING" 比 "STATUS: NORMAL" 长,如果直接覆盖写,从 WARNING 切回 NORMAL 时尾部会残留 G 之类的字符。处理办法是给短字符串补空格,用黑底空格把旧内容擦掉——这也是最简单的"局部清屏"。
六、踩坑记录
- 坐标系不统一,角度符号全错:模块正放方向和代码约定的指向不一致时,
PITCH会变成反的,报警永远触发不了。先把模组摆正、确认 X/Y/Z 朝向后,再对照AZ ≈ 9.8验证一遍。 - 用
asin算倾角会出现跳变:接近 ±90° 时解算不稳定,换成atan2f后平滑很多。 abs不适用于浮点:阈值判断要用fabsf,否则老是得到 0。- 没有回差会疯狂闪屏:状态行在阈值附近来回跳,加 2° 迟滞立刻解决。
- 自检信息上屏最省事:
LCD ID: 33918和绿色IMU OK直接把"屏幕好不好、传感器通没通"变成一上电就能看到的结论,不用插串口。 - I2C 杜邦线接触不良:模组是外挂的,线一松读到的就全是 0 或固定值,状态行会一直
NORMAL,很容易误判成"算法没问题"。读数为 0 时优先检查接线。
七、还能怎么加
现在的报警是"屏幕变红",属于被动提示。如果要做成真正意义上的报警器,可以往下延伸:
- 加蜂鸣器:
WARNING时拉高 GPIO 驱动无源蜂鸣器 PWM,实现声光双报警; - 加 LED 指示:板载 LED 同步闪红,远处也能看到;
- 加输出保持:超限后锁存报警,需要按键复位,适合做倾倒检测/防跌倒;
- 改成相对继电器输出:接一路继电器,用于机柜倾倒断电保护这类场景;
- 补磁力计:YAW 不再漂移,姿态角就完整了。
总结
这个项目的价值不在代码行数,而在于把一条完整的传感器 → 解算 → 决策 → 显示链路落地了:原始数据可视、结果可读、报警可验证,每一步都能自证正确。尤其是 44.7° 不报、46.5° 报警这种边界表现,比"能跑"更能说明实现是对的。
如果这篇文章对你有帮助,欢迎点赞、收藏、评论区交流~
标签:STM32F407、MPU6500、TFTLCD、倾角检测、互补滤波、报警器、HAL库、嵌入式
总代码
/* USER CODE BEGIN Header /
/*
- @file : main.c
- @brief : Main program body
- @attention
- Copyright © 2026 STMicroelectronics.
- All rights reserved.
- This software is licensed under terms that can be found in the LICENSE file
- in the root directory of this software component.
- If no LICENSE file comes with this software, it is provided AS-IS.
/
/ USER CODE END Header /
/ Includes ------------------------------------------------------------------*/
#include “main.h”
/* Private includes ----------------------------------------------------------/
/ USER CODE BEGIN Includes /
#include “app_lcd.h”
#include “app_imu.h”
#include “app_tilt.h”
#include “app_alarm.h”
/ USER CODE END Includes */
/* Private typedef -----------------------------------------------------------/
/ USER CODE BEGIN PTD */
/* USER CODE END PTD */
/* Private define ------------------------------------------------------------/
/ USER CODE BEGIN PD */
/* USER CODE END PD */
/* Private macro -------------------------------------------------------------/
/ USER CODE BEGIN PM */
/* USER CODE END PM */
/* Private variables ---------------------------------------------------------*/
I2C_HandleTypeDef hi2c1;
TIM_HandleTypeDef htim13;
SRAM_HandleTypeDef hsram4;
/* USER CODE BEGIN PV /
static TiltAngles_t s_tilt; / 当前倾角/姿态角 /
static bool s_alarm_on = false; / 告警状态(带 45°/43° 回差) /
/ USER CODE END PV */
/* Private function prototypes -----------------------------------------------/
void SystemClock_Config(void);
static void MX_GPIO_Init(void);
static void MX_FSMC_Init(void);
static void MX_I2C1_Init(void);
static void MX_TIM13_Init(void);
/ USER CODE BEGIN PFP */
/* USER CODE END PFP */
/* Private user code ---------------------------------------------------------/
/ USER CODE BEGIN 0 */
typedef enum { FX_IDLE = 0, FX_FOG_WASH, FX_FULL } FxState_t;
#define FOG_STEP_PX 200u
static FxState_t s_fx_state = FX_IDLE;
static uint16_t s_wash_y = 0;
static uint8_t s_mouth_frame = 0;
static uint8_t s_mouth_timer = 0;
#define FM_SKULL_X 160
#define FM_SKULL_Y 180
#define FM_SKULL_W 160
#define FM_SKULL_H 200
static void draw_static_ui(bool imu_ok)
{
LCD_ShowString(20, 10, “26 ZHI NENG 1 BAN-DI 5 ZU”, LCD_YELLOW, LCD_BLACK);
LCD_ShowString(20, 40, “LCD ID:”, LCD_WHITE, LCD_BLACK);
LCD_ShowInt(110, 40, LCD_GetLastID(), LCD_GRAY, LCD_BLACK);
LCD_ShowString(220, 40, imu_ok ? “IMU OK” : “IMU FAIL”,
imu_ok ? LCD_GREEN : LCD_RED, LCD_BLACK);
LCD_ShowString(20, 270, "TILT ANGLE DEG", LCD_YELLOW, LCD_BLACK);
LCD_ShowString(20, 300, "TX:", LCD_WHITE, LCD_BLACK);
LCD_ShowString(20, 330, "TY:", LCD_WHITE, LCD_BLACK);
LCD_ShowString(20, 360, "TZ:", LCD_WHITE, LCD_BLACK);
LCD_ShowString(20, 400, "ATTITUDE DEG", LCD_YELLOW, LCD_BLACK);
LCD_ShowString(20, 430, "PITCH:", LCD_WHITE, LCD_BLACK);
LCD_ShowString(20, 460, "ROLL:", LCD_WHITE, LCD_BLACK);
LCD_ShowString(20, 490, "YAW:", LCD_WHITE, LCD_BLACK);
LCD_ShowString(20, 530, "STATUS:", LCD_WHITE, LCD_BLACK);
LCD_ShowString(20, 560, "LIMIT 45 DEG", LCD_GRAY, LCD_BLACK);
}
static void draw_fogman_base(void)
{
uint16_t sx = FM_SKULL_X, sy = FM_SKULL_Y;
LCD_FillRect(sx, sy, FM_SKULL_W, FM_SKULL_H, LCD_WHITE);
LCD_FillRect(sx + 20, sy + 10, FM_SKULL_W - 40, 20, LCD_WHITE);
LCD_FillRect(sx + 50, sy, FM_SKULL_W - 100, 30, LCD_WHITE);
LCD_FillRect(sx + 28, sy + 50, 40, 50, LCD_BLACK);
LCD_FillRect(sx + 92, sy + 50, 40, 50, LCD_BLACK);
LCD_FillRect(sx + 42, sy + 72, 12, 10, LCD_YELLOW);
LCD_FillRect(sx + 106, sy + 72, 12, 10, LCD_YELLOW);
LCD_FillRect(sx + 74, sy + 110, 12, 28, LCD_BLACK);
LCD_FillRect(sx + 16, sy + 150, 30, 50, LCD_WHITE);
LCD_FillRect(sx + 114, sy + 150, 30, 50, LCD_WHITE);
}
static void erase_mouth_area(void)
{
uint16_t sx = FM_SKULL_X, sy = FM_SKULL_Y;
LCD_FillRect(sx + 36, sy + 152, 88, 40, LCD_WHITE);
}
static void draw_mouth_closed(void)
{
uint16_t sx = FM_SKULL_X, sy = FM_SKULL_Y;
LCD_FillRect(sx + 46, sy + 158, 68, 4, LCD_BLACK);
}
static void draw_mouth_half(void)
{
uint16_t sx = FM_SKULL_X, sy = FM_SKULL_Y;
LCD_FillRect(sx + 40, sy + 156, 80, 22, LCD_BLACK);
LCD_FillRect(sx + 44, sy + 156, 6, 10, LCD_WHITE);
LCD_FillRect(sx + 56, sy + 156, 6, 12, LCD_WHITE);
LCD_FillRect(sx + 68, sy + 156, 6, 14, LCD_WHITE);
LCD_FillRect(sx + 80, sy + 156, 6, 14, LCD_WHITE);
LCD_FillRect(sx + 92, sy + 156, 6, 12, LCD_WHITE);
LCD_FillRect(sx + 104, sy + 156, 6, 10, LCD_WHITE);
}
static void draw_mouth_open(void)
{
uint16_t sx = FM_SKULL_X, sy = FM_SKULL_Y;
LCD_FillRect(sx + 36, sy + 156, 88, 36, LCD_BLACK);
LCD_FillRect(sx + 40, sy + 156, 6, 18, LCD_WHITE);
LCD_FillRect(sx + 52, sy + 156, 6, 20, LCD_WHITE);
LCD_FillRect(sx + 64, sy + 156, 6, 22, LCD_WHITE);
LCD_FillRect(sx + 76, sy + 156, 6, 22, LCD_WHITE);
LCD_FillRect(sx + 88, sy + 156, 6, 22, LCD_WHITE);
LCD_FillRect(sx + 100, sy + 156, 6, 20, LCD_WHITE);
LCD_FillRect(sx + 112, sy + 156, 6, 18, LCD_WHITE);
LCD_FillRect(sx + 40, sy + 174, 6, 18, LCD_WHITE);
LCD_FillRect(sx + 52, sy + 174, 6, 18, LCD_WHITE);
LCD_FillRect(sx + 64, sy + 174, 6, 18, LCD_WHITE);
LCD_FillRect(sx + 76, sy + 174, 6, 18, LCD_WHITE);
LCD_FillRect(sx + 88, sy + 174, 6, 18, LCD_WHITE);
LCD_FillRect(sx + 100, sy + 174, 6, 18, LCD_WHITE);
LCD_FillRect(sx + 112, sy + 174, 6, 18, LCD_WHITE);
}
static void draw_mouth_frame(uint8_t frame)
{
switch (frame)
{
case 0: draw_mouth_closed(); break;
case 1: draw_mouth_half(); break;
case 2: draw_mouth_open(); break;
case 3: draw_mouth_half(); break;
default: draw_mouth_closed(); break;
}
}
static void screen_fx_start(void)
{
s_fx_state = FX_FOG_WASH;
s_wash_y = 0;
}
static void screen_fx_tick(void)
{
if (s_fx_state == FX_IDLE) return;
if (s_fx_state == FX_FOG_WASH)
{
uint16_t next = s_wash_y + FOG_STEP_PX;
if (next > LCD_HEIGHT) next = LCD_HEIGHT;
LCD_FillRect(0, s_wash_y, LCD_WIDTH, next - s_wash_y, LCD_GRAY);
s_wash_y = next;
if (s_wash_y >= LCD_HEIGHT)
{
s_fx_state = FX_FULL;
s_mouth_frame = 0;
s_mouth_timer = 0;
draw_fogman_base();
draw_mouth_frame(0);
}
}
else if (s_fx_state == FX_FULL)
{
s_mouth_timer++;
if (s_mouth_timer >= 2)
{
s_mouth_timer = 0;
s_mouth_frame = (s_mouth_frame + 1) % 4;
erase_mouth_area();
draw_mouth_frame(s_mouth_frame);
}
}
}
static void screen_fx_stop(bool imu_ok)
{
s_fx_state = FX_IDLE;
s_wash_y = 0;
s_mouth_frame = 0;
s_mouth_timer = 0;
LCD_Clear(LCD_BLACK);
draw_static_ui(imu_ok);
}
/* USER CODE END 0 */
/**
- @brief The application entry point.
- @retval int
*/
int main(void)
{
/* USER CODE BEGIN 1 */
/* USER CODE END 1 */
/* MCU Configuration--------------------------------------------------------*/
/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
HAL_Init();
/* USER CODE BEGIN Init */
/* USER CODE END Init */
/* Configure the system clock */
SystemClock_Config();
/* USER CODE BEGIN SysInit */
/* USER CODE END SysInit */
/* Initialize all configured peripherals /
MX_GPIO_Init();
MX_FSMC_Init();
MX_I2C1_Init();
MX_TIM13_Init();
/ USER CODE BEGIN 2 */
/* LCD 上电:开背光 + 探测 ID + 自适应初始化 + 清屏(黑底) */
LCD_Init();
bool imu_ok = IMU_Init();
Tilt_Init();
Alarm_Init();
draw_static_ui(imu_ok);
/* USER CODE END 2 */
/* Infinite loop /
/ USER CODE BEGIN WHILE /
while (1)
{
/ USER CODE END WHILE */
/* USER CODE BEGIN 3 */
if (imu_ok)
{
ImuSample_t s;
if (IMU_ReadSample(&s))
{
Tilt_Update(&s, &s_tilt);
float limit = s_alarm_on ? 43.0f : 45.0f;
s_alarm_on = (s_tilt.tz > limit);
if (s_alarm_on && s_tilt.tx > 0) {
HAL_GPIO_WritePin(GPIOC, GPIO_PIN_9, GPIO_PIN_SET);
HAL_GPIO_WritePin(GPIOC, GPIO_PIN_7, GPIO_PIN_RESET);
} else if (s_alarm_on && s_tilt.tx < 0) {
HAL_GPIO_WritePin(GPIOC, GPIO_PIN_9, GPIO_PIN_RESET);
HAL_GPIO_WritePin(GPIOC, GPIO_PIN_7, GPIO_PIN_SET);
} else {
HAL_GPIO_WritePin(GPIOC, GPIO_PIN_9, GPIO_PIN_RESET);
HAL_GPIO_WritePin(GPIOC, GPIO_PIN_7, GPIO_PIN_RESET);
}
Alarm_SetSing(s_alarm_on);
Alarm_Update();
if (s_alarm_on)
{
if (s_fx_state == FX_IDLE) screen_fx_start();
screen_fx_tick();
}
else
{
if (s_fx_state != FX_IDLE)
{
screen_fx_stop(true);
}
LCD_ShowString(20, 80, "AX:", LCD_WHITE, LCD_BLACK);
LCD_ShowFloat(56, 80, s.ax, LCD_CYAN, LCD_BLACK);
LCD_ShowString(20, 110, "AY:", LCD_WHITE, LCD_BLACK);
LCD_ShowFloat(56, 110, s.ay, LCD_CYAN, LCD_BLACK);
LCD_ShowString(20, 140, "AZ:", LCD_WHITE, LCD_BLACK);
LCD_ShowFloat(56, 140, s.az, LCD_CYAN, LCD_BLACK);
LCD_ShowString(20, 170, "GX:", LCD_WHITE, LCD_BLACK);
LCD_ShowFloat(56, 170, s.gx, LCD_MAGENTA, LCD_BLACK);
LCD_ShowString(20, 200, "GY:", LCD_WHITE, LCD_BLACK);
LCD_ShowFloat(56, 200, s.gy, LCD_MAGENTA, LCD_BLACK);
LCD_ShowString(20, 230, "GZ:", LCD_WHITE, LCD_BLACK);
LCD_ShowFloat(56, 230, s.gz, LCD_MAGENTA, LCD_BLACK);
LCD_ShowFloat6(64, 300, s_tilt.tx, LCD_CYAN, LCD_BLACK);
LCD_ShowFloat6(64, 330, s_tilt.ty, LCD_CYAN, LCD_BLACK);
LCD_ShowFloat6(64, 360, s_tilt.tz, s_alarm_on ? LCD_RED : LCD_GREEN, LCD_BLACK);
LCD_ShowFloat6(100, 430, s_tilt.pitch, LCD_WHITE, LCD_BLACK);
LCD_ShowFloat6(88, 460, s_tilt.roll, LCD_WHITE, LCD_BLACK);
LCD_ShowFloat6(76, 490, s_tilt.yaw, LCD_GRAY, LCD_BLACK);
LCD_ShowString(108, 530, s_alarm_on ? "WARNING" : "NORMAL ",
s_alarm_on ? LCD_RED : LCD_GREEN, LCD_BLACK);
}
}
}
else
{
HAL_GPIO_WritePin(GPIOC, GPIO_PIN_7|GPIO_PIN_9, GPIO_PIN_RESET);
Alarm_SetSing(false);
Alarm_Update();
if (s_fx_state != FX_IDLE) screen_fx_stop(false);
}
HAL_Delay(100);
}
/* USER CODE END 3 */
}
/**
- @brief System Clock Configuration
- @retval None
*/
void SystemClock_Config(void)
{
RCC_OscInitTypeDef RCC_OscInitStruct = {0};
RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
/** Configure the main internal regulator output voltage
*/
__HAL_RCC_PWR_CLK_ENABLE();
__HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE1);
/** Initializes the RCC Oscillators according to the specified parameters
- in the RCC_OscInitTypeDef structure.
*/
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI;
RCC_OscInitStruct.HSIState = RCC_HSI_ON;
RCC_OscInitStruct.HSICalibrationValue = RCC_HSICALIBRATION_DEFAULT;
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_NONE;
if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
{
Error_Handler();
}
/** Initializes the CPU, AHB and APB buses clocks
*/
RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
|RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_HSI;
RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV1;
RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;
if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_0) != HAL_OK)
{
Error_Handler();
}
}
/**
- @brief I2C1 Initialization Function
- @param None
- @retval None
*/
static void MX_I2C1_Init(void)
{
/* USER CODE BEGIN I2C1_Init 0 */
/* USER CODE END I2C1_Init 0 */
/* USER CODE BEGIN I2C1_Init 1 */
/* USER CODE END I2C1_Init 1 /
hi2c1.Instance = I2C1;
hi2c1.Init.ClockSpeed = 400000;
hi2c1.Init.DutyCycle = I2C_DUTYCYCLE_2;
hi2c1.Init.OwnAddress1 = 0;
hi2c1.Init.AddressingMode = I2C_ADDRESSINGMODE_7BIT;
hi2c1.Init.DualAddressMode = I2C_DUALADDRESS_DISABLE;
hi2c1.Init.OwnAddress2 = 0;
hi2c1.Init.GeneralCallMode = I2C_GENERALCALL_DISABLE;
hi2c1.Init.NoStretchMode = I2C_NOSTRETCH_DISABLE;
if (HAL_I2C_Init(&hi2c1) != HAL_OK)
{
Error_Handler();
}
/ USER CODE BEGIN I2C1_Init 2 */
/* USER CODE END I2C1_Init 2 */
}
/**
- @brief TIM13 Initialization Function
- @param None
- @retval None
*/
static void MX_TIM13_Init(void)
{
/* USER CODE BEGIN TIM13_Init 0 */
/* USER CODE END TIM13_Init 0 */
TIM_OC_InitTypeDef sConfigOC = {0};
/* USER CODE BEGIN TIM13_Init 1 */
/* USER CODE END TIM13_Init 1 /
htim13.Instance = TIM13;
htim13.Init.Prescaler = 0;
htim13.Init.CounterMode = TIM_COUNTERMODE_UP;
htim13.Init.Period = 499;
htim13.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
htim13.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;
if (HAL_TIM_Base_Init(&htim13) != HAL_OK)
{
Error_Handler();
}
if (HAL_TIM_PWM_Init(&htim13) != HAL_OK)
{
Error_Handler();
}
sConfigOC.OCMode = TIM_OCMODE_PWM1;
sConfigOC.Pulse = 250;
sConfigOC.OCPolarity = TIM_OCPOLARITY_HIGH;
sConfigOC.OCFastMode = TIM_OCFAST_DISABLE;
if (HAL_TIM_PWM_ConfigChannel(&htim13, &sConfigOC, TIM_CHANNEL_1) != HAL_OK)
{
Error_Handler();
}
/ USER CODE BEGIN TIM13_Init 2 */
/* USER CODE END TIM13_Init 2 */
HAL_TIM_MspPostInit(&htim13);
}
/**
- @brief GPIO Initialization Function
- @param None
- @retval None
/
static void MX_GPIO_Init(void)
{
GPIO_InitTypeDef GPIO_InitStruct = {0};
/ USER CODE BEGIN MX_GPIO_Init_1 */
/* USER CODE END MX_GPIO_Init_1 */
/* GPIO Ports Clock Enable */
__HAL_RCC_GPIOF_CLK_ENABLE();
__HAL_RCC_GPIOE_CLK_ENABLE();
__HAL_RCC_GPIOD_CLK_ENABLE();
__HAL_RCC_GPIOC_CLK_ENABLE();
__HAL_RCC_GPIOG_CLK_ENABLE();
__HAL_RCC_GPIOB_CLK_ENABLE();
/*Configure GPIO pin Output Level */
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_15, GPIO_PIN_RESET);
HAL_GPIO_WritePin(GPIOC, GPIO_PIN_7|GPIO_PIN_9, GPIO_PIN_RESET);
/*Configure GPIO pin : PB15 —— LCD 背光,推挽输出(LCD_Init 内会置高点亮) */
GPIO_InitStruct.Pin = GPIO_PIN_15;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
/*Configure GPIO pins : PC7 PC9 */
GPIO_InitStruct.Pin = GPIO_PIN_7|GPIO_PIN_9;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);
/* USER CODE BEGIN MX_GPIO_Init_2 */
/* USER CODE END MX_GPIO_Init_2 */
}
/* FSMC initialization function */
static void MX_FSMC_Init(void)
{
/* USER CODE BEGIN FSMC_Init 0 */
/* USER CODE END FSMC_Init 0 */
FSMC_NORSRAM_TimingTypeDef ReadTiming = {0};
FSMC_NORSRAM_TimingTypeDef WriteTiming = {0};
/* USER CODE BEGIN FSMC_Init 1 */
/* USER CODE END FSMC_Init 1 */
/** Perform the SRAM4 memory initialization sequence
- 对齐 pr02-week05 / 正点原子探索者 F407 + 4.3寸 TFT 的标准 FSMC 时序:
-
- ExtendedMode=ENABLE 让读/写各自走 BTR/BWTR 寄存器;
-
- 模式 A(异步 SRAM),ADDHLD 字段不使用,置 0;
-
- CLKDivision / DataLatency 仅同步模式用,模式 A 下保持 0。
/
hsram4.Instance = FSMC_NORSRAM_DEVICE;
hsram4.Extended = FSMC_NORSRAM_EXTENDED_DEVICE;
/ hsram4.Init /
hsram4.Init.NSBank = FSMC_NORSRAM_BANK4;
hsram4.Init.DataAddressMux = FSMC_DATA_ADDRESS_MUX_DISABLE;
hsram4.Init.MemoryType = FSMC_MEMORY_TYPE_SRAM;
hsram4.Init.MemoryDataWidth = FSMC_NORSRAM_MEM_BUS_WIDTH_16;
hsram4.Init.BurstAccessMode = FSMC_BURST_ACCESS_MODE_DISABLE;
hsram4.Init.WaitSignalPolarity = FSMC_WAIT_SIGNAL_POLARITY_LOW;
hsram4.Init.WrapMode = FSMC_WRAP_MODE_DISABLE;
hsram4.Init.WaitSignalActive = FSMC_WAIT_TIMING_BEFORE_WS;
hsram4.Init.WriteOperation = FSMC_WRITE_OPERATION_ENABLE;
hsram4.Init.WaitSignal = FSMC_WAIT_SIGNAL_DISABLE;
hsram4.Init.ExtendedMode = FSMC_EXTENDED_MODE_ENABLE; / 读写分离时序 */
hsram4.Init.AsynchronousWait = FSMC_ASYNCHRONOUS_WAIT_DISABLE;
hsram4.Init.WriteBurst = FSMC_WRITE_BURST_DISABLE;
hsram4.Init.PageSize = FSMC_PAGE_SIZE_NONE;
- CLKDivision / DataLatency 仅同步模式用,模式 A 下保持 0。
/* ReadTiming:读 LCD ID 用,数据保持放宽到 60 HCLK(读时序必须放宽才能读对 ID) */
ReadTiming.AddressSetupTime = 0x0F;
ReadTiming.AddressHoldTime = 0x00;
ReadTiming.DataSetupTime = 60;
ReadTiming.BusTurnAroundDuration = 0;
ReadTiming.CLKDivision = 0;
ReadTiming.DataLatency = 0;
ReadTiming.AccessMode = FSMC_ACCESS_MODE_A;
/* WriteTiming:写时序收紧到 9 HCLK,满足屏控制器写脉宽 >=50ns 要求且刷屏快 */
WriteTiming.AddressSetupTime = 9;
WriteTiming.AddressHoldTime = 0;
WriteTiming.DataSetupTime = 9;
WriteTiming.BusTurnAroundDuration = 0;
WriteTiming.CLKDivision = 0;
WriteTiming.DataLatency = 0;
WriteTiming.AccessMode = FSMC_ACCESS_MODE_A;
if (HAL_SRAM_Init(&hsram4, &ReadTiming, &WriteTiming) != HAL_OK)
{
Error_Handler( );
}
/* USER CODE BEGIN FSMC_Init 2 /
/ FSMC 总线就绪后再给 LCD 控制器一段上电稳定时间,
否则后续写到屏控制器的命令/数据可能丢失。 /
HAL_Delay(50);
/ USER CODE END FSMC_Init 2 */
}
/* USER CODE BEGIN 4 */
/* USER CODE END 4 */
/**
- @brief This function is executed in case of error occurrence.
- @retval None
/
void Error_Handler(void)
{
/ USER CODE BEGIN Error_Handler_Debug /
/ User can add his own implementation to report the HAL error return state /
__disable_irq();
while (1)
{
}
/ USER CODE END Error_Handler_Debug /
}
#ifdef USE_FULL_ASSERT
/* - @brief Reports the name of the source file and the source line number
-
where the assert_param error has occurred. - @param file: pointer to the source file name
- @param line: assert_param error line source number
- @retval None
*/
void assert_failed(uint8_t file, uint32_t line)
{
/ USER CODE BEGIN 6 /
/ User can add his own implementation to report the file name and line number,
ex: printf(“Wrong parameters value: file %s on line %d\r\n”, file, line) /
/ USER CODE END 6 /
}
#endif / USE_FULL_ASSERT */
`
DAMO开发者矩阵,由阿里巴巴达摩院和中国互联网协会联合发起,致力于探讨最前沿的技术趋势与应用成果,搭建高质量的交流与分享平台,推动技术创新与产业应用链接,围绕“人工智能与新型计算”构建开放共享的开发者生态。
更多推荐



所有评论(0)