作为一名嵌入式开发工程师,我经常需要快速搭建STM32开发环境并验证外设功能。今天要分享的是基于STM32F103RCT6的入门级开发流程,重点讲解如何通过Cubemx和Keil搭建基础工程,并实现串口通信与1.8寸SPI屏幕的驱动。这个组合在智能家居控制面板、工业HMI等场景中非常常见。
STM32F103RCT6作为经典的Cortex-M3内核MCU,具有72MHz主频、256KB Flash和48KB RAM,完全能够胜任大多数基础控制任务。而1.8寸SPI屏幕因其体积小、功耗低、接口简单等特点,非常适合作为嵌入式设备的显示终端。通过本文的配置方法,你可以在30分钟内完成从零开始的环境搭建到屏幕显示第一个图案的全过程。
对于这个项目,我们需要准备以下硬件组件:
硬件连接时需特别注意:
注意:不同厂家的屏幕引脚定义可能不同,务必查阅具体产品的数据手册。我曾遇到过一家厂商的DC引脚标注为RS,另一家则标注为A0,接错会导致无法通信。
需要安装的软件工具链包括:
安装时的几个关键点:
启动CubeMX后,按以下步骤操作:
系统时钟配置建议:
我们需要配置USART1用于调试输出:
引脚自动分配应为:
调试心得:建议在项目初期就启用串口调试功能,HAL库提供的printf重定向方法非常实用。我曾在一个项目中因为没有及时配置串口,导致后期调试花费了大量时间。
对于1.8寸SPI屏幕,我们需要配置软件SPI(模拟时序):
屏幕背光控制:
在CubeMX中完成配置后:
打开生成的Keil工程后,需要检查以下设置:
生成的工程包含以下关键文件:
建议的代码组织方式:
为了方便调试,我们重定向printf到串口:
c复制#ifdef __GNUC__
#define PUTCHAR_PROTOTYPE int __io_putchar(int ch)
#else
#define PUTCHAR_PROTOTYPE int fputc(int ch, FILE *f)
#endif
PUTCHAR_PROTOTYPE {
HAL_UART_Transmit(&huart1, (uint8_t *)&ch, 1, HAL_MAX_DELAY);
return ch;
}
编写简单的回显测试程序:
c复制uint8_t rxData;
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart) {
if(huart->Instance == USART1) {
HAL_UART_Transmit(&huart1, &rxData, 1, 100);
HAL_UART_Receive_IT(&huart1, &rxData, 1);
}
}
// 在main()初始化部分添加:
HAL_UART_Receive_IT(&huart1, &rxData, 1);
测试方法:
常见问题:如果收不到回显,检查:
- 串口线是否接反(TX-RX交叉)
- 波特率是否匹配
- 开发板供电是否正常
根据ST7735S数据手册,典型的初始化流程如下:
具体实现代码示例:
c复制void LCD_Init(void) {
// 硬件复位
HAL_GPIO_WritePin(LCD_RST_GPIO_Port, LCD_RST_Pin, GPIO_PIN_RESET);
HAL_Delay(20);
HAL_GPIO_WritePin(LCD_RST_GPIO_Port, LCD_RST_Pin, GPIO_PIN_SET);
HAL_Delay(120);
// 发送初始化命令序列
LCD_WriteCommand(0x01); // Software reset
HAL_Delay(150);
LCD_WriteCommand(0x11); // Sleep out
HAL_Delay(255);
LCD_WriteCommand(0x3A); // Color mode
LCD_WriteData(0x05); // 16-bit/pixel
// ...更多初始化命令
}
由于使用GPIO模拟SPI,需要实现基本时序:
c复制void LCD_WriteCommand(uint8_t cmd) {
HAL_GPIO_WritePin(LCD_DC_GPIO_Port, LCD_DC_Pin, GPIO_PIN_RESET); // 命令模式
HAL_GPIO_WritePin(LCD_CS_GPIO_Port, LCD_CS_Pin, GPIO_PIN_RESET); // 片选
for(uint8_t i=0; i<8; i++) {
HAL_GPIO_WritePin(LCD_SCK_GPIO_Port, LCD_SCK_Pin, GPIO_PIN_RESET);
if(cmd & 0x80)
HAL_GPIO_WritePin(LCD_MOSI_GPIO_Port, LCD_MOSI_Pin, GPIO_PIN_SET);
else
HAL_GPIO_WritePin(LCD_MOSI_GPIO_Port, LCD_MOSI_Pin, GPIO_PIN_RESET);
HAL_GPIO_WritePin(LCD_SCK_GPIO_Port, LCD_SCK_Pin, GPIO_PIN_SET);
cmd <<= 1;
}
HAL_GPIO_WritePin(LCD_CS_GPIO_Port, LCD_CS_Pin, GPIO_PIN_SET);
}
数据传输函数类似,只是DC引脚状态不同。
实现几个基础绘图函数:
c复制// 设置绘制区域
void LCD_SetWindow(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1) {
LCD_WriteCommand(0x2A); // 列地址设置
LCD_WriteData(x0 >> 8); LCD_WriteData(x0 & 0xFF);
LCD_WriteData(x1 >> 8); LCD_WriteData(x1 & 0xFF);
LCD_WriteCommand(0x2B); // 行地址设置
LCD_WriteData(y0 >> 8); LCD_WriteData(y0 & 0xFF);
LCD_WriteData(y1 >> 8); LCD_WriteData(y1 & 0xFF);
LCD_WriteCommand(0x2C); // 内存写入
}
// 绘制像素点
void LCD_DrawPixel(uint16_t x, uint16_t y, uint16_t color) {
LCD_SetWindow(x, y, x, y);
LCD_WriteData(color >> 8);
LCD_WriteData(color & 0xFF);
}
// 填充矩形
void LCD_FillRect(uint16_t x, uint16_t y, uint16_t w, uint16_t h, uint16_t color) {
uint32_t pixels = w * h;
LCD_SetWindow(x, y, x+w-1, y+h-1);
for(; pixels > 0; pixels--) {
LCD_WriteData(color >> 8);
LCD_WriteData(color & 0xFF);
}
}
完整的main.c示例:
c复制int main(void) {
HAL_Init();
SystemClock_Config();
MX_GPIO_Init();
MX_USART1_UART_Init();
printf("System Initialized\r\n");
LCD_Init();
LCD_FillRect(0, 0, 160, 128, RGB(255,0,0)); // 红色背景
LCD_FillRect(20, 20, 120, 80, RGB(0,255,0)); // 绿色矩形
printf("LCD Initialized\r\n");
while (1) {
HAL_Delay(1000);
printf("System running...\r\n");
}
}
批量数据传输优化:
屏幕刷新策略:
延时优化:
排查步骤:
可能原因:
解决方法:
诊断方法:
常见原因:
解决方法:
掌握了基础功能后,可以考虑以下扩展:
在实际项目中,我发现这种1.8寸SPI屏幕非常适合作为设备状态显示器。比如在一个温控器项目中,我们使用类似的配置显示当前温度、设定值和系统状态,整个硬件成本不到50元,却大大提升了产品可用性。