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标题(团队标识)白色
40LCD ID + IMU OK白 / 绿
80 ~ 140AX / AY / AZ(m/s²)白标签 + 青色数值
170 ~ 230GX / GY / GZ(deg/s)白标签 + 品红数值
260TILT ANGLE DEG + TX/TY/TZ黄色标题
320ATTITUDE DEG + PITCH/ROLL/YAW黄色标题
380STATUS: NORMAL / WARNING绿 / 红
405LIMIT 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 的死区后,状态切换就非常干净。

实测数据刚好卡在边界上,也验证了这个判据:

场景PITCHROLL状态显示
水平静置-0.70.6NORMAL
倾斜 44.7°(临界)-44.7-1.0NORMAL
倾斜 46.5°(越界)-8.5-46.5WARNING
大角度翻转-52.0-29.6WARNING

-44.7° 不报警、-46.5° 报警,边界行为和设计一致。

4. 残影:状态行文字长度不一致的坑

"STATUS: WARNING" 比 "STATUS: NORMAL" 长,如果直接覆盖写,从 WARNING 切回 NORMAL 时尾部会残留 G 之类的字符。处理办法是给短字符串补空格,用黑底空格把旧内容擦掉——这也是最简单的"局部清屏"。

六、踩坑记录

  1. 坐标系不统一,角度符号全错:模块正放方向和代码约定的指向不一致时,PITCH 会变成反的,报警永远触发不了。先把模组摆正、确认 X/Y/Z 朝向后,再对照 AZ ≈ 9.8 验证一遍。
  2. 用 asin 算倾角会出现跳变:接近 ±90° 时解算不稳定,换成 atan2f 后平滑很多。
  3. abs 不适用于浮点:阈值判断要用 fabsf,否则老是得到 0。
  4. 没有回差会疯狂闪屏:状态行在阈值附近来回跳,加 2° 迟滞立刻解决。
  5. 自检信息上屏最省事:LCD ID: 33918 和绿色 IMU OK 直接把"屏幕好不好、传感器通没通"变成一上电就能看到的结论,不用插串口。
  6. 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;

/* 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 */

`

Logo

DAMO开发者矩阵,由阿里巴巴达摩院和中国互联网协会联合发起,致力于探讨最前沿的技术趋势与应用成果,搭建高质量的交流与分享平台,推动技术创新与产业应用链接,围绕“人工智能与新型计算”构建开放共享的开发者生态。

更多推荐