ARTICLE DETAIL

资讯详情

深耕网站建设、视觉设计与SEO优化的一线实战洞察。

STM32F103C8T6管脚复用与重映射完全指南

STM32F103C8T6管脚复用与重映射完全指南 玩STM32的人几乎没人能绕开STM32F103C8T6。这颗Cortex-M3内核、72MHz主频的小芯片在DIY圈、课程设计、产品原型里的出镜率太高了64KB Flash、20KB SRAM一个USB、一个CAN、三个串口、两个SPI、两个I2C外设说不上豪华但对绝大多数项目完全够用。尤其是那种十几块钱就能买到的最小系统板晶振、复位、BOOT、USB转串口都做好了插上线就能写程序。但用的人越多问题就越集中。我经常看到有人问为什么PA9不输出为什么串口和PWM功能总是冲突为什么PB4怎么配置都没反应这些问题的根源几乎都指向同一个知识盲区——管脚功能与复用。STM32的每个引脚不只是一个普通输入输出口它可以被映射成串口、PWM、ADC、I2C等外设的接口还可以通过重映射搬到另一组引脚上。搞懂这套机制你才算真正会选型、会布板、会写配置代码。这篇文章我从C8T6的管脚布局讲起把引脚分类、默认复用、重映射机制、常用映射表一次说清楚再配上三个我实际跑过的案例最后聊几个高频踩坑场景。适合正在做毕设、课程设计或者第一次画C8T6板子的人。1. 动手之前先弄清C8T6手里有多少牌1.1 LQFP48封装下到底引出了哪些引脚STM32F103C8T6封装是LQFP48一共48个脚。听起来不少但拆开看就没那么富余了电源、地、复位、启动、调试这些“必备脚”占掉七八个剩下的才是GPIO。官方标称最多37个GPIO但其中很大一部分和其它复用功能共用一个物理引脚所以真正能随心所欲用的数量会随着项目占用而减少。我经常提醒新手一件事不是所有外设功能都被引到了C8T6的封装外面。比如USART2重映射到PD5/PD6但C8T6只引出了PD0、PD1、PD2这三个PD口所以在C8T6上做USART2重映射没有意义。类似的坑还有很多下面的表格里我会专门标注。另一个容易被忽略的点PD0/PD1在多数最小系统板上已经被8MHz外部晶振占用PC14/PC15也被板载的32.768kHz低速晶振占用如果板子做了RTC电路。想把这几个引脚当GPIO用不是不行但前提是你得在电路上放弃对应晶振很多板子根本没留这个余地。1.2 最小系统板上的引脚该怎么理解先回应一个很多新手问过的问题“LQFP48这么小的贴片怎么插到面包板上”答案是插不了。LQFP48是表面贴封装引脚间距只有0.5mm别说面包板普通洞洞板都很难手工焊接。最省事的做法是直接买成品最小系统板它已经帮你把芯片焊好并把所有引脚通过2.54mm排针引出来面包板上插排针、飞杜邦线都非常方便。拿到最小系统板后你会看到两排排针常见的引脚有PA0~PA12、PA15、PB0~PB15、PC13~PC15以及电源、地和BOOT0等。这里要特别强调排针上写着某个引脚标号不代表它当前就是普通IO。例如PA13和PA14上电默认是SWDIO和SWCLKPA15、PB3、PB4上电默认是JTAG相关引脚。在没做任何配置之前把它们当中某些普通IO用自然是“怎么配都没反应”。所以理解引脚不能只看标号还要看它“默认归谁管”。这就引出下面这节。2. 管脚功能全解每一类引脚都有它的脾气2.1 电源、复位和启动引脚先保证芯片能活电源类引脚看起来不起眼却是最多低级故障的来源。C8T6有VDDA/VSSA和VDD/VSS两组供电。为什么不能直接全部接同一个3.3V因为ADC、内部复位电路、PLL这些模拟模块需要相对干净的电源。很多画板新手只接了VDD和VSSVDDA/VSSA悬空结果芯片要么不启动要么ADC采样值跳得离谱。按官方要求VDDA和VSSA必须接且最好通过磁珠或小电感从数字电源分离至少也要加一颗0.1uF去耦电容。VBAT是备用电源引脚给RTC和备份寄存器供电。不用RTC电池时直接接到3.3V即可。NRST是复位脚低电平有效典型接法是10k上拉到3.3V、对地0.1uF电容这个电路几乎所有开发板都在用。启动模式由BOOT0和BOOT1决定。BOOT1其实就是PB2这个要重点记住。复位时芯片采样这两个引脚的逻辑电平BOOT00从主Flash启动这是正常模式BOOT01、BOOT10进入系统存储器也就是ISP串口下载模式BOOT01、BOOT11从SRAM启动。因为BOOT1就是PB2而且很多板子在PB2上接了10k下拉电阻所以设计时尽量不要把PB2当普通输出用否则按键电平、LED电平可能会影响启动模式选择调试时很难排查。2.2 时钟引脚PD0/PD1、PC14/PC15的特殊身份PD0和PD1在C8T6里的默认身份是OSC_IN和OSC_OUT即外部高速晶振HSE的输入输出端。绝大多数最小系统板都焊接了8MHz无源晶振晶振两端通过负载电容典型12pF~20pF接到这两个引脚。所以如果你买到的是带晶振的开发板PD0/PD1已经被占用再想当GPIO用就得考虑硬件上是否可行。PC14和PC15对应OSC32_IN/OSC32_OUT专门给外部32.768kHz低速晶振LSE用。很多板子未必焊这个晶振不焊的话PC14/PC15可以当普通GPIO用。但注意PC13、PC14、PC15这一组引脚比较特殊驱动能力比普通PA/PB口弱不适合直接驱动LED或继电器这类负载做输入或接高阻负载更稳妥。还有个容易被忽略的时钟输出脚PA8可以复用为MCO把内部时钟信号输出到外部。比如从PA8输出系统时钟给其它设备做同步或者做调试观察内部时钟是否正常这个功能在管脚复用中也很有用。2.3 调试接口JTAG/SWD占用了一批好用的引脚C8T6支持SWD和JTAG两种调试方式。SWD只用到PA13SWDIO和PA14SWCLK两根线加复位、地就能连调试器。JTAG则还会用到PA15JTDI、PB3JTDO、PB4JNTRST。问题就出在这里芯片上电默认把这五个引脚全部配置为调试功能如果你想用PB4做普通IO在没释放JTAG之前无论怎么写GPIO寄存器PB4都不会响应你的配置。解决办法是关闭JTAG功能、只保留SWD释放PA15、PB3、PB4。标准库代码如下RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE); GPIO_PinRemapConfig(GPIO_Remap_SWJ_JTAGDisable, ENABLE);如果你用的是CubeMX在SYS配置里把Debug选成Serial Wire生成的代码会自动完成上述配置而且不会影响PA13/PA14的SWD调试功能。这里提醒一句如果配置成完全禁用所有SWJ功能释放PA13/PA14那之后你就连不上调试器了只能通过ISP串口下载救回来操作时要想清楚。3. 管脚复用一个引脚怎么干好几份活3.1 为什么要复用而不是多设计几个引脚芯片封装引脚数量是固定的LQFP48就48个脚。可芯片内部外设太多三个串口、两个SPI、两个I2C、CAN、USB、多个定时器、ADC……每个外设都要占用芯片的IO资源如果每个信号都单独拉一根线出去48个脚远远不够。解决思路很像城市道路一个路口既要过公交又要过出租那就用“信号灯”来调度。STM32的复用就是类似的调度机制。同一个引脚在寄存器里设置成不同模式它就能把不同的内部信号送到外部。比如PA9既可以做普通输出也可以把USART1的发送信号引出来还可以让TIM1的PWM信号从这里输出。这个“信号灯”就是GPIO配置寄存器里的复用模式。但注意一个很容易误解的点复用不是自动的。你使能了USART1PA9并不会自动变成TX脚还必须把PA9配置成复用推挽输出模式。很多新手只开外设时钟、不配GPIO模式结果功能不工作就是这个原因。3.2 默认复用和重映射到底差在哪默认复用Default Alternate Function说的是某个外设信号在出厂设计上“默认”挂在哪个引脚比如USART1_TX默认在PA9SPI1_SCK默认在PA5。你要用这个功能就要把对应引脚配置成对应的复用模式。重映射Remap则灵活得多。通过AFIO_MAPR寄存器可以把某个外设信号“搬迁”到另一组引脚上去。比如USART1可以搬到PB6/PB7SPI1可以搬到PA15/PB3/PB4/PB5CAN可以搬到PB8/PB9。这样做的好处是避开引脚冲突或者让布线更顺畅。使用重映射的标准步骤有三步打开AFIO时钟RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);通过GPIO_PinRemapConfig()函数使能对应外设的重映射。把新的引脚配置为对应复用模式。重映射后原来默认引脚就不再输出这个外设信号了你可以把原引脚解放出来干别的。这在实际项目里非常香也是管脚复用价值最大的地方。3.3 一张表看懂C8T6常用外设的映射关系下面这张表我整理的是C8T6实际可用的映射关系不值得记但建议收藏画板子前翻出来对照一遍。注意我特意把“在C8T6上没有意义”的重映射标注出来避免踩坑。外设功能默认引脚重映射引脚备注USART1_TX / RXPA9 / PA10PB6 / PB7常用重映射后PA9/PA10可干别的USART2_TX / RXPA2 / PA3PD5 / PD6C8T6未引出PD5/PD6此重映射无效USART3_TX / RXPB10 / PB11PC10 / PC11部分完全重映射到PD8/PD9C8T6无效SPI1_NSS / SCK / MISO / MOSIPA4 / PA5 / PA6 / PA7PA15 / PB3 / PB4 / PB5需先释放JTAGI2C1_SCL / SDAPB6 / PB7PB8 / PB9重映射后PB6/PB7让给USART1I2C2_SCL / SDAPB10 / PB11无重映射固定引脚CAN_RX / TXPA11 / PA12PB8 / PB9用USB时PA11/PA12被占CAN可搬走USB_DM / DPPA11 / PA12无重映射固定无备用方案TIM1_CH1 / CH2 / CH3 / CH4PA8 / PA9 / PA10 / PA11有重映射但C8T6常用默认高级定时器功能丰富TIM2_CH1 / CH2 / CH3 / CH4PA0 / PA1 / PA2 / PA3PA15/PB3/PA2/PA3等部分重映射可用TIM3_CH1 / CH2 / CH3 / CH4PA6 / PA7 / PB0 / PB1PB4/PB5/PB0/PB1部分PC6~PC9完全完全重映射在C8T6也有意义TIM4_CH1 / CH2 / CH3 / CH4PB6 / PB7 / PB8 / PB9无重映射引脚固定这张表里最有实战价值的几个重映射是USART1搬去PB6/PB7、SPI1搬去PA15/PB3/PB4/PB5、CAN搬去PB8/PB9、TIM3完全重映射到PC6~PC9。这几个组合能解决绝大多数引脚冲突问题。3.4 有的引脚要“先解锁”才能用重映射还有一个隐藏条件某些备用引脚本身被其它功能锁定。比如SPI1重映射到PA15/PB3/PB4/PB5但这四个引脚原本是JTAG调试引脚必须先释放JTAG才能当SPI1用。操作顺序就是前面提到的开AFIO时钟关闭JTAG再使能SPI1重映射。这个顺序如果搞反了配置会莫名失败。I2C重映射到PB8/PB9就不涉及JTAG问题但PB8/PB9同时是TIM4_CH3/CH4和CAN重映射引脚规划时要一起看。用表格把PIN、外设、复用信号列出来冲突一目了然。4. 三个实战案例把复用用到实处4.1 案例一串口和PWM打架重映射让两边都干活一个典型场景项目里要用PWM调速选用了TIM1_CH2PA9和TIM1_CH3PA10但同时USART1串口默认也在PA9/PA10。两个功能硬撞了。最优雅的解法把USART1重映射到PB6/PB7这样PA9/PA10继续给PWM串口走PB6/PB7两边互不干扰。标准库下配置核心代码大致如下// 1. 打开AFIO时钟 RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE); // 2. 使能USART1重映射 GPIO_PinRemapConfig(GPIO_Remap_USART1, ENABLE); // 3. 重新配置PB6为TX复用推挽 GPIO_InitTypeDef GPIO_InitStructure; GPIO_InitStructure.GPIO_Pin GPIO_Pin_6; GPIO_InitStructure.GPIO_Mode GPIO_Mode_AF_PP; GPIO_InitStructure.GPIO_Speed GPIO_Speed_50MHz; GPIO_Init(GPIOB, GPIO_InitStructure); // 4. PB7为RX输入 GPIO_InitStructure.GPIO_Pin GPIO_Pin_7; GPIO_InitStructure.GPIO_Mode GPIO_Mode_IN_FLOATING; GPIO_Init(GPIOB, GPIO_InitStructure); // 5. 之后正常初始化USART1波特率中断这些都不用改 USART_InitTypeDef USART_InitStructure; // ...照常配置这里有个特别重要的认知重映射只是把外设信号改到另一个物理引脚外设本身没有变。所以你仍然是用USART1这个外设、USART1的中断号、USART1的库函数。很多人改完引脚后不知道怎么继续操作其实完全没影响。4.2 案例二PA8复用TIM1_CH1PWMDMA驱动WS2812最多人问的一个玩法用PA8输出PWMDMA驱动一颗WS2812。为什么要用PA8因为PA8是TIM1_CH1的默认复用引脚不需要任何重映射配置最简单而且在最小系统板上直接有排针引出来接WS2812数据线非常方便。WS2812是单总线协议一个数据位持续约1.25us0码和1码靠高电平时间区分。通常0码高电平约0.35us~0.4us1码高电平约0.7us~0.8us发完一帧后要保持至少50us低电平作为复位。用定时器PWMDMA的思路是让TIM1产生固定频率的PWM每个PWM周期正好对应一个数据位通过DMA把预先算好的CCR值挨个送进CCR寄存器从而在硬件层面把像素数据“发完”CPU从头到尾不用管每一位的时序。关键参数计算TIM1挂在APB2上时钟72MHz。要得到800kHz的PWM频率PSC设为0ARR设为89即72MHz / (01) / (891) 800kHz。每个PWM周期就是1.25us。0码对应的CCR值约为25~29对应0.35us~0.4us高电平1码对应的CCR值约为55~64对应0.77us~0.89us高电平。实际微调时可以先用示波器或逻辑分析仪看波形。核心配置思路如下// PA8配置为复用推挽 GPIO_InitStructure.GPIO_Pin GPIO_Pin_8; GPIO_InitStructure.GPIO_Mode GPIO_Mode_AF_PP; GPIO_InitStructure.GPIO_Speed GPIO_Speed_50MHz; GPIO_Init(GPIOA, GPIO_InitStructure); // TIM1配置PSC0, ARR89 TIM_TimeBaseStructure.TIM_Prescaler 0; TIM_TimeBaseStructure.TIM_CounterMode TIM_CounterMode_Up; TIM_TimeBaseStructure.TIM_Period 89; TIM_TimeBaseStructure.TIM_ClockDivision 0; TIM_TimeBaseInit(TIM1, TIM_TimeBaseStructure); // PWM1模式初始占空比随意 TIM_OCInitStructure.TIM_OCMode TIM_OCMode_PWM1; TIM_OCInitStructure.TIM_OutputState TIM_OutputState_Enable; TIM_OCInitStructure.TIM_Pulse 45; TIM_OC1Init(TIM1, TIM_OCInitStructure); // 高级定时器必须开主输出否则PWM不出去 TIM_CtrlPWMOutputs(TIM1, ENABLE); // 把预装好的CCR数组通过DMA搬进TIM1-CCR1 // 0码数组元素填T0H1码数组元素填T1H DMA_InitStructure.DMA_PeripheralBaseAddr (uint32_t)TIM1-CCR1; DMA_InitStructure.DMA_MemoryBaseAddr (uint32_t)ws2812_buf; DMA_InitStructure.DMA_BufferSize led_count * 24; // ... 配置DMA方向、模式、数据宽度等 DMA_Init(DMA1_Channel2, DMA_InitStructure); // TIM1_CH1对应DMA1_Channel2 TIM_DMACmd(TIM1, TIM_DMA_CC1, ENABLE);这个方案最大的好处是发一帧24位数据只需要一次DMA搬运CPU几乎可以同时做别的任务。很多“智能灯带”、“氛围灯”项目都是这么干的驱动几百颗灯珠也稳定。4.3 案例三矩阵键盘引脚规划提前避开复用冲突做4x4矩阵键盘需要8个GPIO看起来随便选都行但选错了后面就会很痛苦。比如你选了PA6/PA7做行线后面想做PWM发现TIM3_CH1/CH2默认就在PA6/PA7又要重映射一顿折腾。我的规划习惯是先把一定要用的固定外设引脚锁定比如串口的PA9/PA10、SWD的PA13/PA14、USB的PA11/PA12然后用“不碍事”的引脚去做矩阵键盘。推荐方案可以是行线用PB12~PB15列线用PB10、PB11、PD2、PC13或者行线用PA0~PA3列线用PB0~PB3具体看项目里还有没有ADC、定时器需求。扫描方法上如果行线设为推挽输出列线设为上拉输入就可以用内部上拉省掉外部8颗电阻。逐行拉低再读列线就能判断哪个按键被按下。注意PB2尽量不要进矩阵键盘它的BOOT1身份和板载下拉电阻很容易让启动配置受影响。5. 排坑实录这些引脚坑我替你踩过5.1 为什么PB4、PA15怎么配置都没有输出八成是JTAG占用。PB4JNTRST和PA15JTDI上电默认归调试接口管不释放JTAGGPIO配置根本不起作用。解决办法就是前面说过的开AFIO时钟执行GPIO_PinRemapConfig(GPIO_Remap_SWJ_JTAGDisable, ENABLE);释放后PB4/PA15变成普通IO。如果用的是CubeMX在SYS配置里选择Serial Wire代码生成后自动搞定。这里提醒一下如果连SWD也没有了多半是把Debug配置成了Disable重新配置烧录或用ISP下载救回来。5.2 I2C总是通讯失败先检查是不是开漏加没加上拉I2C协议是开漏总线SCL和SDA引脚必须配置成复用开漏模式而且在外部接上拉电阻典型值是4.7k。很多人把PB6/PB7配置成了复用推挽输出结果总线和另一个设备抢电平通讯时好时坏。另外I2C重映射到PB8/PB9后同样要在PB8/PB9外部加上拉。还有一个很隐蔽的问题如果I2C和USART1重映射都争用PB6/PB7你会看到I2C和串口功能互相干扰。这时候要么把I2C搬到PB8/PB9要么把USART1留在PA9/PA10总之一句话引脚规划要在前面做不要等代码写完再调。5.3 外部晶振占用了PD0/PD1想当GPIO怎么办如果你用的是自绘板且不想用外部晶振可以直接用内部HSI 8MHz时钟把PD0/PD1配置成GPIO。但很多最小系统板已经把8MHz晶振焊上去了电路上晶振和负载电容还接着此时强行把PD0/PD1当IO用可能会受晶振电路影响信号不稳定。所以我的建议是板载晶振的PD0/PD1就别惦记了老老实实当晶振脚用自己画板时如果非要释放PD0/PD1就干脆不焊晶振用内部时钟。5.4 不会背引脚没关系学会用CubeMX查引脚可能有人觉得记引脚映射表特别烦。我的习惯是直接用CubeMX和STM32CubeMX的引脚视图选好STM32F103C8Tx后右侧芯片图上点任何一个引脚会弹出该引脚支持的所有复用功能选择串口、SPI、PWM等后芯片图上会高亮所有可用的映射位置还可以直接点击切换默认引脚和重映射引脚。配置完成后生成的代码里GPIO模式、AFIO配置都是自动的比自己翻手册省力得多。但有一点想说清楚工具能帮你自动生成配置却不能替代理解。如果你不知道PA15/PB3/PB4被JTAG占用CubeMX也可能让你“莫名其妙”配不出来。所以我始终建议用工具确认结果但背后的机制还是要懂。5.5 关于国产替代与资源差异很多人买到的“STM32F103C8T6”其实是国产兼容芯片比如GD32F103C8T6、APM32F103C8T6等引脚、封装基本兼容很多情况下可以直接替换。但要注意不同厂家的外设寄存器、ADC精度、USB兼容性会有细微差别尤其是涉及USB、低功耗、ADC采样这类敏感外设时最好实测后再定方案。这不算坑但心里要有个预期。最后再分享一个小技巧我自己画C8T6板子时会先在Excel里建一张表格把所有要用的外设信号列出来再对应到引脚旁边标上“默认/重映射/是否需要释放调试口/是否与其它功能冲突”。画板前花半小时做这件事能省下之后好几天的调试时间。很多人一上来就拉线结果引脚撞车了再回头查远比提前规划麻烦。还有一点C8T6的官方数据手册和参考手册是两本文档引脚定义看数据手册里的Table 4复用映射表看参考手册里的Alternate Function Mapping。网上很多二手资料都省略了“当前封装是否引出这个引脚”这个关键条件所以我建议还是以官方PDF为准再结合最小系统板的原理图一起看。把这两个文件下载到本地比收藏一百篇教程都管用。
返回列表