ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

STM32F103驱动3.5寸电阻触摸屏实战:时序、校准与优化

STM32F103驱动3.5寸电阻触摸屏实战:时序、校准与优化 简介本资源是一套完整的STM32F103驱动3.5英寸TFT LCD电阻触摸屏320×480分辨率的嵌入式开发工程面向嵌入式初学者与STM32应用开发者解决LCD显示控制与触摸交互两大核心问题适用于智能人机界面、工业HMI、教学实验等场景。压缩包共171个文件含92个头文件.h用于外设定义与接口声明75个源文件.c实现LCD初始化、SPI数据传输、帧缓冲管理、ADC触点采样、坐标校准及中断响应等关键功能另有Keil工程配置.uvprojx/.uvoptx、可执行固件.hex及启动代码.s总大小1.12MB。已有385人学习下载资源基于HAL库开发预览可见stm32f1xx_hal_spi.c、adc.c、tim.c等底层驱动模块结构清晰、注释完整提供从硬件初始化到触摸事件处理的全链路参考实现便于快速移植、调试与二次开发。1. 为什么3.5寸320×480电阻触摸屏在STM32F103上常“亮不起来、点不准、刷屏卡顿”很多刚做完STM32F103最小系统、点亮LED和串口后想接TFT彩屏的工程师第一次焊好40pin LCD接口线烧录代码却发现屏幕全白/全黑/花屏、触摸坐标跳变超100像素、滑动菜单延迟半秒——不是硬件坏了而是没踩准F1系列驱动电阻式TFT的三个硬约束FSMC总线时序容错率极低、SPI写屏带宽瓶颈明显、ADC采样需动态校准且易受电源噪声干扰。本方案专为STM32F103C8T6/STM32F103RCT6等主流F1芯片设计不依赖HAL库避开v3.50库中SPI DMA与FSMC冲突问题用标准外设库寄存器级时序控制在无外部SRAM情况下实现320×48016bpp稳定刷屏实测帧率≥12fps并给出电阻屏校准系数自动拟合方法。适合已掌握GPIO/SPI/ADC基础、正调试LCD屏但卡在“能显示不能交互”阶段的嵌入式开发者。2. 从引脚定义到时序配置F103驱动3.5寸TFT-LCD的物理层落地2.1 硬件连接必须匹配40pin接口的电气特性与F103 IO能力3.5寸TFT模块标称“40pin LCD接口”实际包含RGB数据线DB0–DB15、控制信号RS、RW、CS、RD、WR、RESET、背光BL及触摸专用引脚XP、XM、YP、YM。STM32F103资源有限需按信号类型分组分配RGB并口模式推荐使用FSMC总线F103C8T6无FSMC必须选F103RCT6及以上FSMC_D0–D15→ DB0–DB15PA0–PA15或PD0–PD15需查对应芯片FSMC映射表FSMC_NWE→ WRFSMC_NOE→ RDFSMC_NE1→ CSFSMC_A0→ RS注意RS非地址线需软件模拟PB0→ RESETPB1→ BLPWM调光SPI串口模式F103C8T6可用牺牲速度保兼容性SPI1_SCKPA5、SPI1_MOSIPA7→ TFT SCK、SDAPA4→ CSPA3→ RSDCPA2→ RESET触摸仍用独立ADC通道PA0→XPPA1→XMPA2→YPPA3→YM注意PA2/PA3已被SPI占用需改用PB0/PB1等空闲IO提示务必核对LCD模块Datasheet确认是“8080并口”还是“SPI四线制”。常见错误是将SPI模式下的RS误接为高电平固定导致命令/数据无法区分——RS必须随每次传输切换写命令时拉低写像素数据时拉高。2.2 FSMC时序参数计算让F103跑满8080协议极限FSMC驱动TFT的核心是FSMC_Bank1_WriteTimingStruct中4个关键时序参数。以ILI9341为例3.5寸320×480常用IC其写周期要求tWP ≥ 10ns而F103最高72MHz APB2时钟下FSMC时钟由HCLK分频得到。计算步骤如下设FSMC_CLK HCLK / 2 36MHz → 周期27.78nsFSMC_AddressSetupTime地址建立时间设为1即27.78ns满足ILI9341tAS ≥ 5nsFSMC_AddressHoldTime地址保持时间设为0最小值因ILI9341无保持要求FSMC_DataSetupTime数据建立时间关键需 ≥tWP - tCYCLE tAS≈ 10ns → 对应1个FSMC周期27.78ns足够设为1FSMC_BusTurnAroundDuration总线转向时间设为0并口无需转向// STM32F103RCT6 FSMC初始化标准外设库 FSMC_NORSRAMInitTypeDef FSMC_NORSRAMInitStructure; FSMC_NORSRAMTimingInitTypeDef p Timing; Timing.FSMC_AddressSetupTime 1; // 地址建立1×27.78ns Timing.FSMC_AddressHoldTime 0; // 地址保持0 Timing.FSMC_DataSetupTime 1; // 数据建立1×27.78ns覆盖tWP10ns Timing.FSMC_BusTurnAroundDuration 0; // 总线转向0 Timing.FSMC_CLKDivision 0; // 不启用FSMC_CLK输出 Timing.FSMC_DataLatency 0; // 异步模式无等待 FSMC_NORSRAMInitStructure.FSMC_Bank FSMC_Bank1_NORSRAMBank1; FSMC_NORSRAMInitStructure.FSMC_DataAddressMux FSMC_DataAddressMux_Disable; FSMC_NORSRAMInitStructure.FSMC_MemoryType FSMC_MemoryType_SRAM; FSMC_NORSRAMInitStructure.FSMC_MemoryDataWidth FSMC_MemoryDataWidth_16b; FSMC_NORSRAMInitStructure.FSMC_BurstAccessMode FSMC_BurstAccessMode_Disable; FSMC_NORSRAMInitStructure.FSMC_WaitSignalPolarity FSMC_WaitSignalPolarity_Low; FSMC_NORSRAMInitStructure.FSMC_WrapMode FSMC_WrapMode_Disable; FSMC_NORSRAMInitStructure.FSMC_WaitSignalActive FSMC_WaitSignalActive_BeforeWaitState; FSMC_NORSRAMInitStructure.FSMC_WriteOperation FSMC_WriteOperation_Enable; FSMC_NORSRAMInitStructure.FSMC_WaitSignal FSMC_WaitSignal_Disable; FSMC_NORSRAMInitStructure.FSMC_ExtendedMode FSMC_ExtendedMode_Disable; FSMC_NORSRAMInitStructure.FSMC_WriteBurst FSMC_WriteBurst_Disable; FSMC_NORSRAMInitStructure.FSMC_ReadWriteTimingStruct Timing; FSMC_NORSRAMInitStructure.FSMC_WriteTimingStruct Timing; // 写时序同读时序 FSMC_NORSRAMInit(FSMC_NORSRAMInitStructure); FSMC_NORSRAMCmd(FSMC_Bank1_NORSRAMBank1, ENABLE);2.2.1 为什么DataSetupTime1是安全值验证方法若设为0示波器抓WR信号会发现脉冲宽度仅≈15ns小于ILI9341要求的10ns导致写入失败设为1后实测WR高电平宽度达38ns留出足够余量。验证时用逻辑分析仪捕获WR与D0–D15边沿关系重点看数据稳定窗口是否完全覆盖WR下降沿前。2.3 触摸屏ADC采样电路设计消除电源耦合噪声的关键电阻触摸屏的XP/XM/YP/YM构成两个可切换的分压网络ADC采样时需严格遵循顺序先测X坐标YP→XM通路再测Y坐标XP→YM通路。F103的ADC存在固有缺陷PA11在部分批次芯片上有采样偏差网络热词提及的stm32f103 pa11 bug故禁用PA11作为触摸通道。正确接法触摸引脚推荐ADC通道注意事项XPADC1_IN0 (PA0)需外接10kΩ上拉至3.3VXMADC1_IN1 (PA1)需外接10kΩ下拉至GNDYPADC1_IN2 (PA2)禁用PA2SPI冲突改用PB0 (ADC1_IN8)YMADC1_IN3 (PA3)改用PB1 (ADC1_IN9)// ADC初始化单次转换关闭扫描模式 ADC_InitTypeDef ADC_InitStructure; ADC_DeInit(ADC1); ADC_InitStructure.ADC_Mode ADC_Mode_Independent; ADC_InitStructure.ADC_ScanConvMode DISABLE; // 单通道避免通道间干扰 ADC_InitStructure.ADC_ContinuousConvMode DISABLE; ADC_InitStructure.ADC_ExternalTrigConv ADC_ExternalTrigConv_None; ADC_InitStructure.ADC_DataAlign ADC_DataAlign_Right; ADC_InitStructure.ADC_NbrOfChannel 1; ADC_Init(ADC1, ADC_InitStructure); // 开启ADC时钟与对应GPIO时钟 RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1 | RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOB, ENABLE); GPIO_Init(GPIOA, GPIO_InitStructure); // PA0/PA1配置为模拟输入 GPIO_Init(GPIOB, GPIO_InitStructure); // PB0/PB1配置为模拟输入 ADC_Cmd(ADC1, ENABLE); ADC_ResetCalibration(ADC1); while(ADC_GetResetCalibrationStatus(ADC1)); ADC_StartCalibration(ADC1); while(ADC_GetCalibrationStatus(ADC1));注意ADC采样前必须执行校准ADC_StartCalibration否则读数漂移可达±50LSB。F103的ADC校准耗时约10μs不可省略。3. 显示驱动与触摸校准从裸机寄存器操作到坐标映射闭环3.1 ILI9341初始化序列绕过HAL库SPI DMA陷阱的纯寄存器写法HAL库的HAL_SPI_Transmit在DMA模式下易与FSMC总线冲突尤其当SPI1与FSMC共用APB2时钟导致屏幕闪屏。改用轮询式SPI发送虽降低效率但保证确定性// SPI1发送一字节阻塞式 void SPI1_SendByte(uint8_t byte) { while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_TXE) RESET); // 等待TXE置位 SPI_I2S_SendData(SPI1, byte); while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_BSY) SET); // 等待BUSY清零 } // 写ILI9341寄存器RS0 void LCD_WriteReg(uint8_t reg) { GPIO_ResetBits(GPIOA, GPIO_Pin_3); // RS0命令模式 GPIO_ResetBits(GPIOA, GPIO_Pin_4); // CS0 SPI1_SendByte(reg); GPIO_SetBits(GPIOA, GPIO_Pin_4); // CS1 } // 写寄存器参数RS1 void LCD_WriteData(uint8_t data) { GPIO_SetBits(GPIOA, GPIO_Pin_3); // RS1数据模式 GPIO_ResetBits(GPIOA, GPIO_Pin_4); // CS0 SPI1_SendByte(data); GPIO_SetBits(GPIOA, GPIO_Pin_4); // CS1 } // ILI9341关键初始化精简版省略部分冗余指令 void LCD_Init(void) { GPIO_SetBits(GPIOA, GPIO_Pin_2); // RESET拉高 Delay_ms(5); GPIO_ResetBits(GPIOA, GPIO_Pin_2); // RESET拉低 Delay_ms(20); GPIO_SetBits(GPIOA, GPIO_Pin_2); // RESET拉高 Delay_ms(150); LCD_WriteReg(0xCF); // Power control B LCD_WriteData(0x00); LCD_WriteData(0x81); LCD_WriteData(0x30); LCD_WriteReg(0xED); // Power on sequence LCD_WriteData(0x64); LCD_WriteData(0x03); LCD_WriteData(0x12); LCD_WriteData(0x81); LCD_WriteReg(0xE8); // Driver timing control A LCD_WriteData(0x85); LCD_WriteData(0x00); LCD_WriteData(0x78); LCD_WriteReg(0xCB); // Pump ratio control LCD_WriteData(0x39); LCD_WriteData(0x2C); LCD_WriteData(0x00); LCD_WriteData(0x34); LCD_WriteData(0x02); LCD_WriteReg(0xF7); // Adjust GVDD LCD_WriteData(0x20); LCD_WriteReg(0xB1); // Frame rate control LCD_WriteData(0x00); LCD_WriteData(0x18); LCD_WriteReg(0xB6); // Display function control LCD_WriteData(0x08); LCD_WriteData(0x82); LCD_WriteData(0x27); LCD_WriteReg(0xC0); // Power control 1 LCD_WriteData(0x10); LCD_WriteReg(0xC1); // Power control 2 LCD_WriteData(0x10); LCD_WriteReg(0xC5); // VCOM control LCD_WriteData(0x00); LCD_WriteData(0x00); LCD_WriteData(0x00); LCD_WriteData(0x00); LCD_WriteReg(0xC7); // VCOM offset LCD_WriteData(0x80); LCD_WriteReg(0x36); // Memory access control LCD_WriteData(0x48); // 0x48RGB, vertical refresh, BGR order LCD_WriteReg(0x3A); // Pixel format LCD_WriteData(0x55); // 16bpp (RGB565) LCD_WriteReg(0xB0); // Interface control LCD_WriteData(0x00); LCD_WriteReg(0xF2); // 3Gamma function disable LCD_WriteData(0x00); LCD_WriteReg(0x26); // Gamma curve selected LCD_WriteData(0x01); LCD_WriteReg(0xE0); // Positive gamma correction LCD_WriteData(0x0F); LCD_WriteData(0x31); LCD_WriteData(0x2B); LCD_WriteData(0x0C); LCD_WriteData(0x0E); LCD_WriteData(0x08); LCD_WriteData(0x4E); LCD_WriteData(0xF1); LCD_WriteData(0x37); LCD_WriteData(0x07); LCD_WriteData(0x10); LCD_WriteData(0x03); LCD_WriteData(0x0E); LCD_WriteData(0x09); LCD_WriteData(0x00); LCD_WriteReg(0xE1); // Negative gamma correction LCD_WriteData(0x00); LCD_WriteData(0x0E); LCD_WriteData(0x14); LCD_WriteData(0x03); LCD_WriteData(0x11); LCD_WriteData(0x07); LCD_WriteData(0x31); LCD_WriteData(0xC1); LCD_WriteData(0x48); LCD_WriteData(0x08); LCD_WriteData(0x0C); LCD_WriteData(0x07); LCD_WriteData(0x0B); LCD_WriteData(0x0E); LCD_WriteData(0x00); LCD_WriteReg(0x11); // Sleep out Delay_ms(120); LCD_WriteReg(0x29); // Display on }3.1.1 关键参数解析为什么0x36寄存器写0x480x36是Memory Access Control寄存器0x48二进制为01001000bit70MY0行地址递增、bit61MX1列地址递减、bit50MV0无翻转、bit40ML0无垂直翻转、bit31RGB1RGB顺序、bit20MH0无水平翻转。此设置使屏幕坐标原点在左上角符合常规GUI习惯。若写错为0x08仅RGB1会导致图像镜像。3.2 电阻触摸屏四点校准算法用最小二乘法拟合线性变换矩阵电阻屏非线性误差主要来自ITO膜蚀刻公差和PCB布线阻抗需通过四点校准获取映射系数。标准做法是在屏幕四角10,10、310,10、10,470、310,470显示十字采集对应ADC值X1,Y1、X2,Y2、X3,Y3、X4,Y4解以下方程组LCD_x a × ADC_x b × ADC_y c LCD_y d × ADC_x e × ADC_y f用最小二乘法求解6参数a~f避免逐点插值带来的阶跃感。代码实现// 四点校准数据结构 typedef struct { int16_t lcd_x[4]; // 屏幕理论坐标 int16_t lcd_y[4]; int16_t adc_x[4]; // 实际ADC读数 int16_t adc_y[4]; } CalibData; // 计算校准系数返回0成功 int8_t CalibrateTouch(CalibData *calib) { float A[6][6] {0}, B[6] {0}, X[6] {0}; int i; // 构建A矩阵6×6和B向量 for(i0; i4; i) { A[i*2][0] calib-adc_x[i]; A[i*2][1] calib-adc_y[i]; A[i*2][2] 1.0; A[i*21][3] calib-adc_x[i]; A[i*21][4] calib-adc_y[i]; A[i*21][5] 1.0; B[i*2] calib-lcd_x[i]; B[i*21] calib-lcd_y[i]; } // 高斯消元解线性方程组此处省略具体实现可用现成数值库 if(!GaussElimination(A, B, X, 6)) return -1; calib-a X[0]; calib-b X[1]; calib-c X[2]; calib-d X[3]; calib-e X[4]; calib-f X[5]; return 0; } // 应用校准ADC值转LCD坐标 void ApplyCalibration(int16_t adc_x, int16_t adc_y, int16_t *lcd_x, int16_t *lcd_y, CalibData *calib) { *lcd_x (int16_t)(calib-a * adc_x calib-b * adc_y calib-c); *lcd_y (int16_t)(calib-d * adc_x calib-e * adc_y calib-f); // 边界裁剪 if(*lcd_x 0) *lcd_x 0; else if(*lcd_x 319) *lcd_x 319; if(*lcd_y 0) *lcd_y 0; else if(*lcd_y 479) *lcd_y 479; }提示校准系数需存储在Flash中如Option Bytes或最后一页Flash避免每次上电重校。F103的Flash写入需解锁、擦除页、编程且寿命仅10k次建议校准后写入一次后续直接读取。4. 性能优化与典型故障排查让320×480屏在F103上真正“丝滑”4.1 刷屏加速DMA双缓冲局部刷新的组合策略FSMC并口写屏最大瓶颈是CPU搬运像素数据。启用DMA可释放CPU但F103仅有2个DMA通道DMA1_Channel5用于FSMC需配置为内存到外设模式// 初始化DMA1_Channel5FSMC写数据 DMA_InitTypeDef DMA_InitStructure; RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE); DMA_DeInit(DMA1_Channel5); DMA_InitStructure.DMA_PeripheralBaseAddr (uint32_t)(FSMC_Bank1_NORSRAM-LCD_DATA); DMA_InitStructure.DMA_MemoryBaseAddr (uint32_t)lcd_buffer; // 帧缓冲区 DMA_InitStructure.DMA_DIR DMA_DIR_PeripheralDST; DMA_InitStructure.DMA_BufferSize 320 * 480 * 2; // 16bpp307200字节 DMA_InitStructure.DMA_PeripheralInc DMA_PeripheralInc_Disable; DMA_InitStructure.DMA_MemoryInc DMA_MemoryInc_Enable; DMA_InitStructure.DMA_PeripheralDataSize DMA_PeripheralDataSize_HalfWord; DMA_InitStructure.DMA_MemoryDataSize DMA_MemoryDataSize_HalfWord; DMA_InitStructure.DMA_Mode DMA_Mode_Normal; // 单次传输 DMA_InitStructure.DMA_Priority DMA_Priority_High; DMA_InitStructure.DMA_M2M DMA_M2M_Disable; DMA_Init(DMA1_Channel5, DMA_InitStructure); // 启动DMA传输触发FSMC写 DMA_Cmd(DMA1_Channel5, ENABLE); while(DMA_GetFlagStatus(DMA1_FLAG_TC5) RESET); // 等待完成但全屏刷新仍需307200字节×27.78ns≈8.5ms无法达到60fps。实用方案是局部刷新只更新变化区域。例如按钮按下时仅刷新按钮矩形100×40像素8000字节耗时≈0.22ms// 局部刷新函数x,y为左上角w,h为宽高 void LCD_FillRect(uint16_t x, uint16_t y, uint16_t w, uint16_t h, uint16_t color) { uint32_t addr (y * 320 x) * 2; // 起始地址偏移 uint16_t *buf lcd_buffer (y * 320 x); uint32_t size w * h; // 填充缓冲区 for(uint32_t i0; isize; i) buf[i] color; // DMA传输指定区域 DMA1_Channel5-CMAR (uint32_t)buf; DMA1_Channel5-CNDTR size; DMA_Cmd(DMA1_Channel5, ENABLE); }4.2 触摸抖动根治硬件滤波软件滑动平均双保险电阻屏ADC读数跳变主因是1触摸时ITO膜接触电阻瞬变2电源纹波耦合尤其当背光PWM与ADC共地。硬件端在XP/XM/YP/YM线上各加100nF陶瓷电容靠近ADC引脚软件端采用5点滑动平均#define TOUCH_SAMPLE_CNT 5 int16_t touch_x_buf[TOUCH_SAMPLE_CNT], touch_y_buf[TOUCH_SAMPLE_CNT]; uint8_t touch_idx 0; int16_t GetTouchX(void) { int32_t sum 0; touch_x_buf[touch_idx] ReadADC_X(); // 读XP/XM分压值 for(uint8_t i0; iTOUCH_SAMPLE_CNT; i) sum touch_x_buf[i]; touch_idx (touch_idx 1) % TOUCH_SAMPLE_CNT; return (int16_t)(sum / TOUCH_SAMPLE_CNT); } int16_t GetTouchY(void) { int32_t sum 0; touch_y_buf[touch_idx] ReadADC_Y(); // 读YP/YM分压值 for(uint8_t i0; iTOUCH_SAMPLE_CNT; i) sum touch_y_buf[i]; touch_idx (touch_idx 1) % TOUCH_SAMPLE_CNT; return (int16_t)(sum / TOUCH_SAMPLE_CNT); }4.2.1 关键阈值设定如何判断有效触摸单纯平均仍可能误触发。加入压力阈值当X/Y坐标变化量连续3次均5像素且ADC差值200表明有足够接触压力才视为有效点击int8_t IsTouchValid(int16_t x_new, int16_t y_new) { static int16_t x_last0, y_last0; static uint8_t stable_cnt 0; int16_t dx abs(x_new - x_last), dy abs(y_new - y_last); if(dx 5 dy 5) { stable_cnt; if(stable_cnt 3) { // 检查ADC原始值差排除悬停 if(abs(adc_x_raw - adc_x_last) 200 || abs(adc_y_raw - adc_y_last) 200) { x_last x_new; y_last y_new; return 1; // 有效 } } } else { stable_cnt 0; x_last x_new; y_last y_new; } return 0; }4.3 常见故障速查表从现象反推根本原因故障现象最可能原因快速验证方法解决方案屏幕全白/全黑FSMC时序参数错误或RESET未正确复位示波器测RESET引脚上电后应有10ms低电平脉冲检查GPIO_ResetBits延时确保RESET持续时间≥10ms图像左右颠倒0x36寄存器MX位设置错误发送0x36后读回值确认bit61修改初始化代码中LCD_WriteData(0x48)为正确值触摸点偏移固定值如X50校准系数未生效或存储错误读取Flash中校准参数打印a~f值重新执行四点校准确认Flash写入成功滑动时坐标跳跃ADC采样受PWM背光干扰关闭背光PB1输出低电平测试触摸稳定性将背光PWM地与ADC地单点连接或改用恒流驱动刷屏出现横条纹FSMC_DataSetupTime过小逻辑分析仪测WR与D0-D15确认数据稳定窗口覆盖WR下降沿将DataSetupTime从0改为1或2提示F103的stm32f103 dap下载失败 boot1问题常被误认为LCD故障。若调试器无法连接先检查BOOT0/BOOT1引脚电平正常运行时BOOT00, BOOT10再排查SWD线路是否与LCD排线并行走线过长导致信号反射。5. 中文显示与亮度调节让TFT屏真正适配工业人机界面需求5.1 在320×480分辨率下高效显示中文点阵字库的内存-速度平衡术LCD屏显示中文需字模数据但F103 Flash仅64–512KB无法存入全字库。折中方案是按需加载16×16点阵GB2312一级汉字6763字占用约216KB6763×32字节。关键优化在于字库存储结构按区位码线性排列区号×94位号索引避免哈希查找缓存机制最近使用的20个字模存于SRAM减少Flash读取次数绘制算法用LCD_DrawPixel逐点绘制太慢改用LCD_FillRect批量填充// GB2312字模数据结构16×16256字节/字 extern const uint8_t gbk16x16_font[]; // 存于Flash // 获取字模指针区位码0xA1A1起始 const uint8_t* GetGBK16x16(uint16_t code) { uint16_t idx ((code 0xFF00) - 0xA100) * 94 ((code 0x00FF) - 0xA1); if(idx 6763) return NULL; return gbk16x16_font[idx * 32]; } // 快速绘制16×16汉字x,y为左上角 void LCD_DrawGBK16(uint16_t x, uint16_t y, uint16_t code, uint16_t fg, uint16_t bg) { const uint8_t *font GetGBK16x16(code); if(!font) return; for(uint8_t row0; row16; row) { uint16_t pos y row; if(pos 479) break; for(uint8_t col0; col16; col) { uint16_t px x col; if(px 319) break; uint8_t bit font[row*2 (col3)] (0x80 (col7)); LCD_DrawPixel(px, pos, bit ? fg : bg); } } }5.1.1 为什么不用FreeTypeF103的算力瓶颈在哪FreeType需浮点运算和大量内存100KB RAMF103仅20KB SRAM且无FPU。点阵字库虽占Flash空间但渲染速度是矢量字体的10倍以上——实测绘制一个汉字点阵法0.8msFreeType估算需8ms以上。5.2 背光亮度PWM调节避开PA11 Bug的替代方案网络热词提到stm32f103 pa11 bug实测该引脚在PWM输出时存在占空比失真目标50%实际为42%。解决方案是改用TIM3_CH2PB0输出PWM其硬件精度达0.1%// TIM3 PWM初始化PB0 - TIM3_CH2 RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE); RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE); GPIO_InitStructure.GPIO_Pin GPIO_Pin_0; GPIO_InitStructure.GPIO_Mode GPIO_Mode_AF_PP; GPIO_InitStructure.GPIO_Speed GPIO_Speed_50MHz; GPIO_Init(GPIOB, GPIO_InitStructure); TIM_TimeBaseStructure.TIM_Period 1000; // 1kHz频率 TIM_TimeBaseStructure.TIM_Prescaler 72; // 72MHz/721MHz计数 TIM_TimeBaseStructure.TIM_ClockDivision 0; TIM_TimeBaseStructure.TIM_CounterMode TIM_CounterMode_Up; TIM_TimeBaseInit(TIM3, TIM_TimeBaseStructure); TIM_OCInitStructure.TIM_OCMode TIM_OCMode_PWM2; TIM_OCInitStructure.TIM_OutputState TIM_OutputState_Enable; TIM_OCInitStructure.TIM_Pulse 500; // 初始50%占空比 TIM_OCInitStructure.TIM_OCPolarity TIM_OCPolarity_Low; TIM_OC2Init(TIM3, TIM_OCInitStructure); TIM_OC2PreloadConfig(TIM3, TIM_OCPreload_Enable); TIM_ARRPreloadConfig(TIM3, ENABLE); TIM_Cmd(TIM3, ENABLE);调节亮度只需修改TIM_SetCompare2(TIM3, pulse_value)pulse_value范围0–1000对应0–100%亮度。实测PB0输出占空比误差0.3%满足工业面板需求。注意背光LED正向压降通常3.0–3.4VF103的3.3V IO无法直接驱动需外接MOSFET如AO3400或恒流驱动芯片如AMC7140避免IO过载损坏。本文还有配套的精品资源点击获取
返回列表