一、硬件介绍
本次开发使用的是STM32F429I-DISC1开发板,是基于STM32F429ZIT6U的MCU,具有Arm Cortex-M4内核,配备2.4英寸QVGA TFT LCD、64-Mbit SDRAM、ST-MEMS陀螺仪(I3G4250D)等。
陀螺仪MEMS(ST-MEMS I3G4250D)
I3G4250D 是一款低功耗、三轴角速率传感器;I3G4250D 具有 ±245 / ±500 / ±2000 dps 的满量程,并且能够以用户可选择的带宽测量速率。STM32F429ZIT6 通过SPI_5接口控制此运动传感器。

主要特性:
| 输出类型 |
I2C / SPI |
| 特性 |
可调带宽,可选量程,温度传感器 |
| 封装/外壳 |
16-TFLGA |
| 工作温度 |
-40°C ~ 85°C(TA) |
| 轴 |
X(俯仰),Y(偏转),Z(横滚) |
| 电流-供电 |
6.1mA |
| 范围°/s |
±245,500,2000 |
| 灵敏度(mV/°/s) |
- |
| 带宽 |
- |
| 电压-供电 |
2.4V ~ 3.6V |
| 灵敏度(LSB/(°/s)) |
8.75 ~ 70 |
| 类型 |
数字 |
| 数据 |
• 16-bit 原始数据 <br />** |
• 8-bit 温度数据** |
SPI通讯时序图:

原理图:
若要选择 SPI接口,CS线必须低电平。
INT2:数据就绪 / FIFO中断
INT1:可编程中断
CS:SPI / I2C模式选择
0: SPI
1: I2C
传感器 -> MCU硬件连接:
SPI5_SCK -> PF7
SPI5_MISO -> PF8
SPI5_MOSI -> PF9
MEMS_CS -> PC1
MEMS_INT2 -> PA2 (可配置)
MEMS_INT1 -> PA1 (可配置)

2.4寸TFT_LCD
2.4寸TFT LCD模块, 240x320 RGB全彩TFT液晶模块,内建ILI9341控制器, 工作电源电压:2.4V至3.3V;
RGB接口的屏幕是通过MCU不断发送显示数据到屏幕,逐行扫描显示。
VSYNC: 帧同步信号,表示扫描1帧的开始,一帧也就是LCD显示的一个画面。
HSYNC: 行同步信号,表示扫描1行的开始。
VDEN:数据使能信号。
IM[0..3] = 0110 --> 4-wire 8-bit serial I, SDA:In/Out
4 线 8 位串行接口(SDA 双向)
| 信号名称 |
说明 |
| R[7:0] |
红色数据 |
| G[7:0] |
绿色数据 |
| B[7:0] |
蓝色数据 |
| DOTCLK |
像素同步时钟信号 |
| HSYNC |
水平同步信号 |
| VSYNC |
垂直同步信号 |
| ENABLE |
数据使能信号 |

原理图:



二、功能实现思想
实现效果:根据移动开发板的方向以及速度,小球同样以相应的速度向对应的方位移动;
三轴传感器
1、配置I3G4250D传感器,将其通过SPI与STM32F429ZIT6U进行通讯,用于获取传感器数据;
2、移动开发板时,传感器获取此时的角度信息,将数据保存用于小球位置控制;
LCD屏幕
1、配置LCD屏幕初始化,将其通过LTDC驱动;
2、在LCD屏幕上显示小球(初始于中间位置);
3、将陀螺仪获取的数据与小球的位置进行绑定;


三、功能实现
三轴传感器配置
1、配置I3G4250D传感器相关功能;
未配置INT1 / INT2引脚(若有需要可自行配置)



2、配置串口相关功能;

LCD屏幕配置
1、LTDC 配置



需按原理图重新复用相关引脚

2、GPIO 配置
配置相关LCD IO口;

3、时钟树 配置

代码编写
新建LCD文件夹用于存储相关LCD驱动代码;
相关BSP

1、陀螺仪相关代码
i3g4250d_read_data_polling.c 在原来基础上,新增以下代码;
stmdev_ctx_t dev_ctx;
void I3G4250D_Init(void){
dev_ctx.write_reg = platform_write;
dev_ctx.read_reg = platform_read;
dev_ctx.handle = &SENSOR_BUS;
platform_init();
platform_delay(BOOT_TIME);
i3g4250d_device_id_get(&dev_ctx, &whoamI);
if (whoamI != I3G4250D_ID)
printf("Mems_Init_Error\r\n");
else
printf("Mems_Init_Sucess\r\n");
i3g4250d_filter_path_set(&dev_ctx, I3G4250D_LPF1_HP_ON_OUT);
i3g4250d_hp_bandwidth_set(&dev_ctx, I3G4250D_HP_LEVEL_3);
i3g4250d_data_rate_set(&dev_ctx, I3G4250D_ODR_100Hz); //10ms
}
//获取 X / Y角速度数据
void Get_I3G4250D_Data(int16_t* X,int16_t* Y){
uint8_t reg;
i3g4250d_flag_data_ready_get(&dev_ctx, ®);
if (reg) {
memset(data_raw_angular_rate, 0x00, 3 * sizeof(int16_t));
i3g4250d_angular_rate_raw_get(&dev_ctx, data_raw_angular_rate);
angular_rate_mdps[0] = i3g4250d_from_fs245dps_to_mdps(data_raw_angular_rate[0]) / 1000.0f;
angular_rate_mdps[1] = i3g4250d_from_fs245dps_to_mdps(data_raw_angular_rate[1]) / 1000.0f;
*X = (int)angular_rate_mdps[1];
*Y = (int)angular_rate_mdps[0];
}
}
2、LCD相关代码
1.打开…\STM32CubeF4\Drivers\BSP\Components\ili9341.c;
在此基础上重新定义以下相关接口函数;
extern SPI_HandleTypeDef hspi5;
void LCD_IO_Init(void){
LCD_Delay(100);
}
//写数据
void LCD_IO_WriteData(uint16_t RegValue){
HAL_GPIO_WritePin(LCD_WRX_GPIO_Port, LCD_WRX_Pin, GPIO_PIN_SET);
HAL_GPIO_WritePin(LCD_CS_GPIO_Port, LCD_CS_Pin, GPIO_PIN_RESET);
HAL_SPI_Transmit(&hspi5,&RegValue,1,HAL_MAX_DELAY);
HAL_GPIO_WritePin(LCD_CS_GPIO_Port, LCD_CS_Pin, GPIO_PIN_SET);
}
//写命令
void LCD_IO_WriteReg(uint8_t Reg){
HAL_GPIO_WritePin(LCD_WRX_GPIO_Port, LCD_WRX_Pin, GPIO_PIN_RESET);
HAL_GPIO_WritePin(LCD_CS_GPIO_Port, LCD_CS_Pin, GPIO_PIN_RESET);
HAL_SPI_Transmit(&hspi5,&Reg,1,HAL_MAX_DELAY);
HAL_GPIO_WritePin(LCD_CS_GPIO_Port, LCD_CS_Pin, GPIO_PIN_SET);
}
//读数据
uint32_t LCD_IO_ReadData(uint16_t RegValue, uint8_t ReadSize){
HAL_GPIO_WritePin(LCD_WRX_GPIO_Port, LCD_WRX_Pin, GPIO_PIN_SET);
HAL_GPIO_WritePin(LCD_CS_GPIO_Port, LCD_CS_Pin, GPIO_PIN_RESET);
HAL_SPI_Receive(&hspi5,&RegValue,ReadSize,HAL_MAX_DELAY);
HAL_GPIO_WritePin(LCD_CS_GPIO_Port, LCD_CS_Pin, GPIO_PIN_SET);
}
void LCD_Delay(uint32_t delay) {
HAL_Delay(delay); // 直接使用HAL库延时
}
2.创建小球
…\LCD\pic 存储要实现的小球图像,并将其转换为C数组 Ball[ ];
与ltdc.c中的 pLayerCfg.FBStartAdress = (uint32_t)&Ball; 与之相对应;
RGB565 【Layer 0 - Pixel Format】
const unsigned char Ball[4616] = { 0X00,0X10,0X30,0X00,0X30,0X00,0X01,0X1B,
…. }
3、main.c 主要代码
/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */
__IO uint32_t ReloadFlag = 0;
static int16_t Xpos0 = 96;
static int16_t Ypos0 = 136;
static int16_t X_Angle_Rate = 0;
static int16_t Y_Angle_Rate = 0;
static int16_t Width;
static int16_t Height;
#define Width_Max 192
#define Width_Min 0
#define Height_Max 272
#define Height_Min 0
//计算当前位置
static void PicturesPosition(int16_t* x0, int16_t* y0, int16_t X_Angle_Rate, int16_t Y_Angle_Rate)
{
//计算当前位置
*x0 = Xpos0 - X_Angle_Rate;
*y0 = Ypos0 - Y_Angle_Rate;
}
static void Ball_Move(void)
{
//获取角速度
Get_I3G4250D_Data(&X_Angle_Rate,&Y_Angle_Rate);
if(X_Angle_Rate > 50) X_Angle_Rate = 50;
else if (X_Angle_Rate < -50) X_Angle_Rate = -50;
if(Y_Angle_Rate > 50) Y_Angle_Rate = 50;
else if (Y_Angle_Rate < -50) Y_Angle_Rate = -50;
//计算当前位置
PicturesPosition(&Xpos0,&Ypos0,X_Angle_Rate,Y_Angle_Rate);
Width = Xpos0;
Height = Ypos0;
if(Width > Width_Max) Xpos0 = Width_Max;
else if(Width < Width_Min) Xpos0 = Width_Min;
if(Height > Height_Max) Ypos0 = Height_Max;
else if(Height < Height_Min) Ypos0 = Height_Min;
HAL_LTDC_SetWindowPosition_NoReload(&hltdc, Xpos0, Ypos0, 0);
ReloadFlag = 0;
HAL_LTDC_Reload(&hltdc,LTDC_SRCR_VBR); //更新
while(ReloadFlag == 0);//等待完成
HAL_Delay(20);
}
/* Reload Event callback */
void HAL_LTDC_ReloadEventCallback(LTDC_HandleTypeDef *hltdc)
{
ReloadFlag = 1;
}
/* USER CODE END 0 */
int main(void)
{
...
I3G4250D_Init();
ili9341_Init();
while (1)
{
Ball_Move();
}
/* USER CODE END 3 */
}
四、实现现象
根据移动开发板的方向以及速度,小球同样以相应的速度向对应的方位移动;
