1. 引言
在STM32嵌入式开发中,直接操作寄存器或反复调用标准库函数虽然灵活,但代码复用性差、可读性不高,对新手尤其不友好——稍不留神漏掉一条时钟使能或初始化指令,功能就无法实现,排查起来也很费劲。
本文分享一套面向新手的 STM32 外设简易封装库,把 GPIO、输入捕获(测频/测占空比)、编码器接口、ADC 采集、DMA 传输等常用外设的初始化与读取封装成一个个函数。你只需要照着注释填参数、按顺序调用即可,不用关心底层寄存器细节,也不用担心漏写配置指令。后续还会持续补充 SPI、I2C、看门狗等更多外设的简易配置,逐步形成一套属于自己的「STM32 快速开发工具箱」。
📦配套资源下载:本文配套的完整工程代码已打包上传,可通过夸克网盘获取:
- 链接:https://pan.quark.cn/s/95809e5ce13b?pwd=Gjmb
- 提取码:
Gjmb打开「夸克网盘APP」即可下载
stm32_1.zip,内含本文所有封装函数的可直接运行的工程模板。
2. GPIO 驱动封装
GPIO(通用输入输出)是最基础的外设。这里以初始化函数为例,其余函数功能简述如下。
2.1 GPIO 初始化函数
/** * 函 数:GPIO初始化配置 * 参 数:a - GPIO端口指针(如GPIOA) * 参 数:b - GPIO引脚号(如GPIO_Pin_0) * 参 数:GPIO_Mode - GPIO工作模式(如GPIO_Mode_AF_PP) * 返 回 值:无 */voidgpiostart(GPIO_TypeDef*a,uint16_tb,GPIOMode_TypeDef GPIO_Mode){GPIO_InitTypeDef gpio_a;gpio_a.GPIO_Mode=GPIO_Mode;gpio_a.GPIO_Pin=b;gpio_a.GPIO_Speed=GPIO_Speed_50MHz;GPIO_Init(a,&gpio_a);GPIO_ResetBits(a,b);}说明:该函数将端口、引脚、模式作为参数传入,内部统一配置50MHz速度,并在初始化后默认输出低电平。调用示例:
gpiostart(GPIOA,GPIO_Pin_0,GPIO_Mode_Out_PP);// PA0推挽输出2.2 其他 GPIO 封装函数
gpioblink(a, b, c, GPIO_Mode):完成一次“高-低-高”的闪烁动作,适合LED指示或简单脉冲输出。gpiochange(GPIO_Remap1, GPIO_Remap2, GPIO_Remap3):引脚重映射配置。注意:会占用AFIO并可能关闭JTAG/SWD调试口,非必要不要使用。keytim(a, b, c):按键定时扫描,返回按键状态。keyzhuan(a, b, c, d):按键状态转换/消抖处理。
调用示例(示例代码后续将随资源包更新补充):
// LED闪烁:PA0推挽输出,每200ms翻转一次gpioblink(GPIOA,GPIO_Pin_0,200,GPIO_Mode_Out_PP);// 按键扫描:PA1上拉输入,每10ms扫描一次intkey_state=keytim(GPIOA,GPIO_Pin_1,10);if(key_state==1){// 按键按下,执行对应操作}3. 串口(USART)驱动封装
串口是嵌入式开发中最常用的调试与通信手段。这里以 USART1(PA9=TX,PA10=RX)为例,封装了初始化、发送、以及两种实用的帧解析功能。
3.1 串口初始化
/** * 函 数:USART1 PA9(TX)/PA10(RX) GPIO初始化 * 参 数:无 * 返 回 值:无 * 说 明:PA9复用推挽输出,PA10上拉输入 */voidserial_init_u1_pa910(void){RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE);RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);GPIO_InitTypeDef GPIO_InitStructure;GPIO_InitStructure.GPIO_Mode=GPIO_Mode_AF_PP;GPIO_InitStructure.GPIO_Pin=GPIO_Pin_9;GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;GPIO_Init(GPIOA,&GPIO_InitStructure);GPIO_InitStructure.GPIO_Mode=GPIO_Mode_IPU;GPIO_InitStructure.GPIO_Pin=GPIO_Pin_10;GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;GPIO_Init(GPIOA,&GPIO_InitStructure);}/** * 函 数:USART参数初始化配置 * 参 数:USART_BaudRate - 波特率 * 参 数:USART_HardwareFlowControl - 硬件流控制 * 参 数:USART_Mode - 工作模式(发送/接收) * 参 数:USART_Parity - 校验位 * 参 数:USART_StopBits - 停止位 * 参 数:USART_WordLength - 数据位长度 * 返 回 值:无 */voidusart_init_txd(uint16_tUSART_BaudRate,uint16_tUSART_HardwareFlowControl,uint16_tUSART_Mode,uint16_tUSART_Parity,uint16_tUSART_StopBits,uint16_tUSART_WordLength){USART_InitTypeDef a;a.USART_BaudRate=USART_BaudRate;a.USART_HardwareFlowControl=USART_HardwareFlowControl;a.USART_Mode=USART_Mode;a.USART_Parity=USART_Parity;a.USART_StopBits=USART_StopBits;a.USART_WordLength=USART_WordLength;USART_Init(USART1,&a);USART_Cmd(USART1,ENABLE);}说明:serial_init_u1_pa910()完成引脚复用配置,usart_init_txd()完成串口参数配置。调用示例:
serial_init_u1_pa910();usart_init_txd(115200,USART_HardwareFlowControl_None,USART_Mode_Rx|USART_Mode_Tx,USART_Parity_No,USART_StopBits_1,USART_WordLength_8b);3.2 串口发送函数
/** * 函 数:串口发送单个字节(底层函数) * 参 数:USARTx - USART指针 * 参 数:Data - 待发送数据 * 返 回 值:无 */voidchuankou(USART_TypeDef*USARTx,uint16_tData){USART_SendData(USARTx,Data);while(USART_GetFlagStatus(USARTx,USART_FLAG_TXE)==RESET);}/** * 函 数:串口发送字节数组 * 参 数:USARTx - USART指针 * 参 数:Array - 数组首地址 * 参 数:Length - 数组长度 * 返 回 值:无 */voidchuankou_SendArray(USART_TypeDef*USARTx,uint8_t*Array,uint16_tLength){uint16_ti;for(i=0;i<Length;i++){chuankou(USARTx,Array[i]);}}/** * 函 数:串口发送字符串 * 参 数:USARTx - USART指针 * 参 数:String - 字符串指针 * 返 回 值:无 */voidchuankou_SendString(USART_TypeDef*USARTx,char*String){uint8_ti;for(i=0;String[i]!='\0';i++){chuankou(USARTx,String[i]);}}说明:三个发送函数由底层到高层依次封装,分别支持发送单字节、字节数组和字符串,满足不同场景需求。
3.3 字符帧解析(帧格式:'@' + 字符串 + '\r')
staticuint8_tchar_restat=0;staticuint8_tchar_index=0;chartack_char[100];/** * 函 数:字符帧启动并解析 * 参 数:first_byte - 已读取的帧头字节('@') * 返 回 值:无 * 说 明:帧格式 '@' + 字符串 + '\r' */staticvoidchar_frame_start(uint8_tfirst_byte){char_restat=1;char_index=0;}staticvoiduart_parse_char(uint8_tbyte){if(char_restat==1){if(byte=='\r'){tack_char[char_index]='\0';// 字符串结束符char_restat=0;S=1;// 通知主循环}else{if(char_index<99){tack_char[char_index]=byte;char_index++;}// 超长截断,防止溢出}}}说明:以'@'作为帧头,遇到'\r'表示一帧结束,解析结果存入tack_char并置位标志S,主循环检测到后即可处理。
3.4 数值帧解析(帧格式:0xFF + 4字节数据 + 0xFE)
staticuint8_tnum_restat=0;staticuint8_tnum_buf[4];staticuint8_tnum_index=0;uint8_ttack_data[4];/** * 函 数:数值帧启动并解析 * 参 数:first_byte - 已读取的帧头字节(0xFF) * 返 回 值:无 * 说 明:帧格式 0xFF + 4字节数据 + 0xFE */staticvoidnum_frame_start(uint8_tfirst_byte){num_restat=1;num_index=0;}staticvoiduart_parse_num(uint8_tbyte){if(num_restat==1){num_buf[num_index]=byte;num_index++;if(num_index>=4){num_restat=2;num_index=0;}}elseif(num_restat==2){if(byte==0xFE){// 接收完成!拷贝到全局数据区for(inti=0;i<4;i++){tack_data[i]=num_buf[i];}num_restat=0;S=1;// 通知主循环}else{// 帧尾不对,重置num_restat=0;}}}说明:以0xFF作为帧头,接收 4 字节数据后等待0xFE帧尾,校验通过后存入tack_data并置位标志S。这种定长帧格式非常适合传输传感器数据或控制指令。
3.5 其他串口封装函数
Serial_GetRxFlag():获取串口接收标志位并自动清零,返回 1 表示收到新数据。Serial_SendByte(Byte):发送单个字节并等待发送完成。ustar_rxd():接收数据回传测试,将收到的数据原样发回。us_send_U1()/us_send2_U1():串口发送封装,配合中断处理函数使用。USART1_IRQHandler_char()/USART1_IRQHandler_num():中断接收处理,分别调用字符帧或数值帧解析。
注意:帧解析函数需要在USART1_IRQHandler中断服务函数中调用,将接收到的每个字节喂给对应的解析函数。
4. 定时器与 PWM 驱动封装
定时器是 STM32 中最常用的外设之一,既能做定时中断,也能输出 PWM 波形驱动电机、舵机等。这里封装了定时器初始化、PWM 输出与动态调节等函数。
4.1 定时器内部时钟初始化
/** * 函 数:TIM定时器内部时钟初始化(APB1) * 参 数:RCC_APB1Periph_ - APB1外设时钟使能宏(如RCC_APB1Periph_TIM2) * 参 数:TIMX - 定时器指针(如TIM2) * 参 数:TIM_ClockDivision - 时钟分频因子(如TIM_CKD_DIV1) * 参 数:TIM_CounterMode - 计数模式(如TIM_CounterMode_Up) * 参 数:ARR - 自动重装载值 * 参 数:PSC - 预分频器值 * 参 数:TIM_RepetitionCounter - 重复计数器值(高级定时器用,通用填0) * 返 回 值:无 * 说 明:溢出频率 = 主频 / (PSC+1) / (ARR+1) */voidtimer_init_APB1_in(uint32_tRCC_APB1Periph_,TIM_TypeDef*TIMX,uint16_tTIM_ClockDivision,uint16_tTIM_CounterMode,intARR,intPSC,intTIM_RepetitionCounter){RCC_APB1PeriphClockCmd(RCC_APB1Periph_,ENABLE);TIM_InternalClockConfig(TIMX);TIM_TimeBaseInitTypeDef TIM_TimeBaseinitstructure;TIM_TimeBaseinitstructure.TIM_ClockDivision=TIM_ClockDivision;TIM_TimeBaseinitstructure.TIM_CounterMode=TIM_CounterMode;TIM_TimeBaseinitstructure.TIM_Period=ARR;TIM_TimeBaseinitstructure.TIM_Prescaler=PSC;TIM_TimeBaseinitstructure.TIM_RepetitionCounter=TIM_RepetitionCounter;TIM_TimeBaseInit(TIMX,&TIM_TimeBaseinitstructure);}说明:该函数把时钟使能、内部时钟配置、时基初始化全部封装好,只需传入 ARR 和 PSC 即可确定定时周期。配合timer_init_CLeaR(TIMX, TIM_IT_Update)清除标志并开启更新中断,即可实现定时中断功能。
4.2 PWM 输出与动态调节
/** * 函 数:PWM输出开启配置 * 参 数:TIM_OCMode - 输出比较模式(如TIM_OCMode_PWM1) * 参 数:TIM_OCPolarity - 输出极性(如TIM_OCPolarity_High) * 参 数:CCR - 捕获/比较寄存器初始值(占空比) * 参 数:TIMX - 定时器指针 * 返 回 值:无 */voidPWEL_OPEN(uint16_tTIM_OCMode,uint16_tTIM_OCPolarity,intCCR,TIM_TypeDef*TIMX){TIM_OCInitTypeDef a;TIM_OCStructInit(&a);a.TIM_OCMode=TIM_OCMode;a.TIM_OCPolarity=TIM_OCPolarity;a.TIM_OutputState=TIM_OutputState_Enable;a.TIM_Pulse=CCR;TIM_OC1Init(TIMX,&a);TIM_Cmd(TIMX,ENABLE);}/** * 函 数:PWM占空比动态设置(修改CCR) * 参 数:TIMX - 定时器指针 * 参 数:Compare - 比较值(占空比 = Compare / (ARR+1)) * 返 回 值:无 */voidPWM_CCR(TIM_TypeDef*TIMX,uint16_tCompare){TIM_SetCompare1(TIMX,Compare);}说明:PWEL_OPEN完成 PWM 通道1的开启,PWM_CCR可在运行中动态修改占空比,非常适合电机调速、舵机角度控制等场景。另有PWM_PSC(TIMx, Prescaler, TIM_PSCReloadMode)用于动态修改预分频值,timer_outAPB1支持外部时钟(ETR)模式,timer_init_APB1_ENCODER用于编码器计数场景。
调用示例(示例代码后续将随资源包更新补充):
// 初始化TIM2,72MHz / (72-1) / (1000-1) ≈ 1kHz 溢出频率timer_init_APB1_in(RCC_APB1Periph_TIM2,TIM2,TIM_CKD_DIV1,TIM_CounterMode_Up,1000-1,72-1,0);// 开启PWM1模式,初始占空比50%PWEL_OPEN(TIM_OCMode_PWM1,TIM_OCPolarity_High,500,TIM2);// 运行中动态调整占空比到25%PWM_CCR(TIM2,250);5. 输入捕获驱动封装
输入捕获常用于测量外部信号的频率和占空比。这里以 TIM3 为例,封装了初始化、从模式触发和读取函数。
5.1 输入捕获初始化
/** * 函 数:输入捕获初始化配置 * 参 数:TIMx - 定时器指针(如TIM3) * 参 数:TIM_Channel - 捕获通道(如TIM_Channel_1) * 参 数:TIM_ICFilter - 输入捕获滤波器值 * 参 数:TIM_ICPolarity - 捕获极性(如TIM_ICPolarity_Rising) * 参 数:TIM_ICPrescaler - 捕获预分频 * 参 数:TIM_ICSelection - 输入通道映射选择 * 参 数:j - 模式选择(0=普通输入捕获,1=PWM输入模式) * 返 回 值:无 */voidic_init(TIM_TypeDef*TIMx,uint16_tTIM_Channel,intTIM_ICFilter,uint16_tTIM_ICPolarity,uint16_tTIM_ICPrescaler,uint16_tTIM_ICSelection,intj){TIM_ICInitTypeDef a;a.TIM_Channel=TIM_Channel;a.TIM_ICFilter=TIM_ICFilter;a.TIM_ICPolarity=TIM_ICPolarity;a.TIM_ICPrescaler=TIM_ICPrescaler;a.TIM_ICSelection=TIM_ICSelection;TIM_ICInit(TIMx,&a);if(j==1){TIM_PWMIConfig(TIMx,&a);// PWM输入模式,自动配置双通道}}说明:j=1时使用PWM输入模式,可同时测量周期和脉宽。
5.2 其他输入捕获封装函数
IC_RES(TIMx, TIM_InputTriggerSource, TIM_SlaveMode):配置从模式触发源并启动定时器。IC_GetFreq():返回频率值(Hz),公式1MHz / (CCR+1)。IC_Getduty():返回占空比(0~100%),基于 CCR2 脉宽与 CCR1 周期计算。
调用示例(示例代码后续将随资源包更新补充):
// 初始化TIM3_CH1输入捕获,上升沿触发,普通模式ic_init(TIM3,TIM_Channel_1,0xF,TIM_ICPolarity_Rising,TIM_ICPSC_DIV1,TIM_ICSelection_DirectTI,0);// 配置从模式触发并启动定时器IC_RES(TIM3,TIM_TS_TI1FP1,TIM_SlaveMode_Reset);// 主循环中读取频率和占空比uint32_tfreq=IC_GetFreq();// 频率,单位Hzuint32_tduty=IC_Getduty();// 占空比,0~100%注意:以上读取函数硬编码了 TIM3,若需移植到其他定时器,请将TIM3替换为对应的定时器指针。
6. ADC 驱动封装
ADC(模数转换器)用于将模拟电压转换为数字量,是传感器采集的核心外设。这里以 ADC1 为例,封装了规则组初始化、校准、单次转换和多次采样取平均等函数。
6.1 ADC 规则组初始化
/** * 函 数:ADC规则组通道初始化配置 * 参 数:RCC_APB2Periph_ADCX - ADC外设时钟(如RCC_APB2Periph_ADC1) * 参 数:RCC_PCLK2 - ADC时钟分频配置(如RCC_PCLK2_Div6) * 参 数:ADCx - ADC指针(如ADC1) * 参 数:ADC_Channel - ADC通道号 * 参 数:Rank - 规则组排序位置 * 参 数:ADC_SampleTime - 采样时间 * 参 数:ADC_Mode - ADC工作模式 * 参 数:ADC_DataAlign - 数据对齐方式 * 参 数:ADC_ExternalTrigConv - 外部触发转换源 * 参 数:ADC_NbrOfChannel - 规则组通道总数 * 参 数:ADC_ContinuousConvMode - 连续转换模式 * 参 数:ADC_ScanConvMode - 扫描模式 * 返 回 值:无 */voidAD_init_Regular(uint32_tRCC_APB2Periph_ADCX,uint32_tRCC_PCLK2,ADC_TypeDef*ADCx,uint8_tADC_Channel,uint8_tRank,uint8_tADC_SampleTime,uint32_tADC_Mode,uint32_tADC_DataAlign,uint32_tADC_ExternalTrigConv,intADC_NbrOfChannel,uint32_tADC_ContinuousConvMode,uint32_tADC_ScanConvMode){RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADCX,ENABLE);RCC_ADCCLKConfig(RCC_PCLK2);ADC_RegularChannelConfig(ADCx,ADC_Channel,Rank,ADC_SampleTime);ADC_InitTypeDef A;A.ADC_ContinuousConvMode=ADC_ContinuousConvMode;A.ADC_DataAlign=ADC_DataAlign;A.ADC_ExternalTrigConv=ADC_ExternalTrigConv;A.ADC_Mode=ADC_Mode;A.ADC_NbrOfChannel=ADC_NbrOfChannel;A.ADC_ScanConvMode=ADC_ScanConvMode;ADC_Init(ADCx,&A);ADC_Cmd(ADCx,ENABLE);}说明:该函数将时钟、通道、采样时间、工作模式等全部参数化。若需采集多个通道,需在ADC_RegularChannelConfig中依次配置各通道的排序位置。
6.2 其他 ADC 封装函数
adc_Calibrate(ADCx):上电后建议先执行校准,可有效消除ADC内部的偏移误差,提升转换精度。AD_num(ADCx, ADC_FLAG):软件触发单次转换,返回12位原始值(0~4095)。V_num(ADCx, ADC_FLAG):将原始值换算为放大100倍的电压值(如330表示3.30V),便于显示或串口打印。AD_avenum(ADCx, ADC_FLAG, a):对同一通道连续采样a次并取平均,可有效抑制随机噪声,适合对精度要求较高的传感器采集场景。
调用示例(示例代码后续将随资源包更新补充):
// 初始化ADC1通道0(PA0),采样时间55.5周期AD_init_Regular(RCC_APB2Periph_ADC1,RCC_PCLK2_Div6,ADC1,ADC_Channel_0,1,ADC_SampleTime_55Cycles5,ADC_Mode_Independent,ADC_DataAlign_Right,ADC_ExternalTrigConv_None,1,DISABLE,DISABLE);// 上电校准adc_Calibrate(ADC1);// 单次读取原始值和电压值uint16_traw=AD_num(ADC1,ADC_FLAG_EOC);// 原始值 0~4095uint16_tvolt=V_num(ADC1,ADC_FLAG_EOC);// 电压值,如330表示3.30V// 多次采样取平均(采样10次)uint16_tavg=AD_avenum(ADC1,ADC_FLAG_EOC,10);7. DMA 驱动封装
DMA(直接存储器访问)可在不占用CPU的情况下完成外设与存储器之间的数据搬运,常用于ADC连续采集、串口收发等场景。这里封装了DMA通道初始化和单次传输等待完成两个函数。
7.1 DMA 通道初始化
/** * 函 数:DMA通道初始化配置 * 参 数:RCC_AHBPeriph - AHB外设时钟(如RCC_AHBPeriph_DMA1) * 参 数:DMA_PeripheralBaseAddr - 外设基地址 * 参 数:DMA_PeripheralDataSize - 外设数据宽度(如DMA_PeripheralDataSize_Byte) * 参 数:DMA_PeripheralInc - 外设地址是否自增(如DMA_PeripheralInc_Enable) * 参 数:DMA_BufferSize - 传输数据量(缓冲区大小) * 参 数:DMA_MemoryBaseAddr - 存储器基地址 * 参 数:DMA_MemoryDataSize - 存储器数据宽度 * 参 数:DMA_MemoryInc - 存储器地址是否自增 * 参 数:DMA_DIR - 传输方向(如DMA_DIR_PeripheralDST / DMA_DIR_PeripheralSRC) * 参 数:DMA_M2M - 存储器到存储器模式使能 * 参 数:DMA_Mode - 工作模式(如DMA_Mode_Normal / DMA_Mode_Circular) * 参 数:DMA_Priority - 通道优先级 * 参 数:DMAy_Channelx - DMA通道指针(如DMA1_Channel1) * 返 回 值:无 */voidDMA_inT(uint32_tRCC_AHBPeriph,uint32_tDMA_PeripheralBaseAddr,uint32_tDMA_PeripheralDataSize,uint32_tDMA_PeripheralInc,uint32_tDMA_BufferSize,uint32_tDMA_MemoryBaseAddr,uint32_tDMA_MemoryDataSize,uint32_tDMA_MemoryInc,uint32_tDMA_DIR,uint32_tDMA_M2M,uint32_tDMA_Mode,uint32_tDMA_Priority,DMA_Channel_TypeDef*DMAy_Channelx){RCC_AHBPeriphClockCmd(RCC_AHBPeriph,ENABLE);DMA_InitTypeDef a;a.DMA_PeripheralBaseAddr=DMA_PeripheralBaseAddr;a.DMA_PeripheralDataSize=DMA_PeripheralDataSize;a.DMA_PeripheralInc=DMA_PeripheralInc;a.DMA_BufferSize=DMA_BufferSize;a.DMA_MemoryBaseAddr=DMA_MemoryBaseAddr;a.DMA_MemoryDataSize=DMA_MemoryDataSize;a.DMA_MemoryInc=DMA_MemoryInc;a.DMA_DIR=DMA_DIR;a.DMA_M2M=DMA_M2M;a.DMA_Mode=DMA_Mode;a.DMA_Priority=DMA_Priority;DMA_Init(DMAy_Channelx,&a);DMA_Cmd(DMAy_Channelx,ENABLE);}说明:该函数将DMA的时钟、地址、数据宽度、方向、模式、优先级等全部参数化,一次调用即可完成通道配置并启动。
7.2 DMA 单次传输并等待完成
DMA_TRANFER(DMAy_Channelx, DataNumber, DMAy_FLAG):完成一次完整的单次传输流程:先关闭DMA、设置本次传输的数据量、重新开启、等待传输完成标志置位,最后清除标志。适合需要精确控制每次传输数据量的场景。
调用示例(示例代码后续将随资源包更新补充):
// 初始化DMA1通道1:外设到内存,字节宽度,普通模式DMA_inT(RCC_AHBPeriph_DMA1,(uint32_t)&ADC1->DR,// 外设基地址:ADC1数据寄存器DMA_PeripheralDataSize_Byte,DMA_PeripheralInc_Disable,4,// 缓冲区大小:4字节(uint32_t)adc_buffer,// 存储器基地址DMA_MemoryDataSize_Byte,DMA_MemoryInc_Enable,DMA_DIR_PeripheralSRC,// 外设→内存DMA_M2M_Disable,DMA_Mode_Normal,DMA_Priority_High,DMA1_Channel1);// 启动一次传输并等待完成DMA_TRANFER(DMA1_Channel1,4,DMA1_FLAG_TC1);8. 编码器接口驱动封装
编码器接口用于读取正交编码器的转速和方向,常用于电机测速。新手只需调用初始化函数并读取计数值即可,无需理解正交解码的底层原理。
encoder(TIMx, TIM_EncoderMode, TIM_IC1Polarity, TIM_IC2Polarity):初始化编码器接口并启动定时器。encoder_get(TIMx):返回有符号16位计数值(正转递增,反转递减)。
调用示例(示例代码后续将随资源包更新补充):
// 初始化TIM2编码器接口,TI1+TI2双边沿计数encoder(TIM2,TIM_EncoderMode_TI12,TIM_ICPolarity_Rising,TIM_ICPolarity_Rising);// 主循环中读取当前计数值int16_tcount=encoder_get(TIM2);if(count>0){// 正转}elseif(count<0){// 反转}8. 总结
本文通过封装 GPIO、输入捕获、编码器接口、ADC 和 DMA,展示了如何用函数化思维简化 STM32 外设操作。这套封装特别适合新手,具有以下优点:
- 参数化:端口、引脚、模式、通道、地址等均作为参数传入,灵活适配不同硬件。
- 防漏配置:时钟使能、初始化、模式设置等关键步骤全部封装在函数内部,新手按注释填参即可,不会漏写指令。
- 可复用:一次封装,多处调用,减少重复代码。
- 易维护:修改外设配置只需调整调用参数,无需改动驱动内部逻辑。
后续将持续补充 SPI、I2C、看门狗等更多外设的简易配置。读者可根据实际项目需求,在此基础上逐步构建属于自己的 STM32 快速开发库。
资源获取
本文配套工程代码已打包为stm32_1.zip,可通过夸克网盘下载:
- 链接:https://pan.quark.cn/s/95809e5ce13b?pwd=Gjmb
- 提取码:
Gjmb
打开「夸克网盘APP」,复制链接或输入提取码即可获取。下载后可直接导入 Keil 工程,对照本文各章节函数进行调用练习。