如何快速移植(从STM32F103到STM32F407)
最近用到F4的地方比较多,网上代码还是F1多一些,便需要移植代码,如何快速移植代码呢?
看下面这篇文章
外设
首先就是STM32的外设了。
STM32F407ZGT6的基本外设
STM32F407ZGT6 作为 MCU,该芯片是
STM32F407 里面配置非常强大的了,它拥有的资源包括:集成 FPU 和 DSP 指令,并具有 192KB
SRAM、1024KB FLASH、12 个 16 位定时器、2 个 32 位定时器、2 个 DMA 控制器(共 16 个通道)、3 个 SPI、2 个全双工 I2S、3 个 IIC、6 个串口、2 个 USB(支持 HOST /SLAVE)、2 个CAN、3 个 12 位 ADC、2 个 12 位 DAC、1 个 RTC(带日历功能)、1 个 SDIO 接口、1 个 FSMC接口、1 个 10/100M 以太网 MAC 控制器、1 个摄像头接口、1 个硬件随机数生成器、以及 112个通用 IO 口等。该芯片的配置十分强悍,很多功能相对 STM32F1 来说进行了重大改进,比如
FSMC 的速度,F4 刷屏速度可达 3300W 像素/秒,而 F1 的速度则只有 500W 左右。
STM32F103ZET6的基本外设
STM32F103ZETT6 作为 MCU,该芯片是
STM32F103 里面配置非常强大的了,它拥有的资源包括:64KB SRAM、512KB FLASH、2 个基本定时器、4 个通用定时器、2 个高级定时器、2 个 DMA 控制器(共 12 个通道)、3 个 SPI、2 个 IIC、5 个串口、1 个 USB、1 个 CAN、3 个 12 位 ADC、1 个 12 位 DAC、1 个 SDIO 接口、1 个 FSMC 接口以及 112 个通用 IO 口。该芯片的配置十分强悍,并且还带外部总线(FSMC)
可以用来外扩 SRAM 和连接 LCD 等,通过 FSMC 驱动 LCD,可以显著提高 LCD 的刷屏速度,是 STM32F1 家族常用型号里面,最高配置的芯片了
然后是比较重要的一个功能。
时钟
为什么说时钟重要呢,在做SPI或者一些对时的时候,这个时候时钟的重要性就突显出来了。
F407的时钟
SYSCLK(系统时钟) =168MHz
AHB 总线时钟(HCLK=SYSCLK) =168MHz
APB1 总线时钟(PCLK1=SYSCLK/4) =42MHz
APB2 总线时钟(PCLK2=SYSCLK/2) =84MHz
PLL 主时钟 =168MHz
F103的时钟
SYSCLK(系统时钟) =72MHz
AHB 总线时钟(使用 SYSCLK) =72MHz
APB1 总线时钟(PCLK1=SYSCLK/2) =36MHz
APB2 总线时钟(PCLK2) =72MHz
PLL 时钟 =72MHz
IO的配置
1、输入浮空
2、输入上拉
3、输入下拉
4、模拟输入
5、开漏输出
6、推挽输出
7、推挽式复用功能
8、开漏式复用功能
模式就不用说了,这个大家应该都知道,重要的是如何配置。
F407
一定要注意使能IO口时钟,而且要写对 博主就踩了几次坑
使能 IO 口时钟。调用函数为 RCC_AHB1PeriphClockCmd ()
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOF, ENABLE);//使能GPIOF时钟
初始化 IO 参数。调用函数 GPIO_Init();GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;//普通输出模式GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;//推挽输出GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;//100MHzGPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;//上拉GPIO_Init(GPIOF, &GPIO_InitStructure);//初始化GPIO
上面的是输出配置,下面看一下输入配置,大家可以比较一下。
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2|GPIO_Pin_3|GPIO_Pin_4; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN;//普通输入模式GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;//100MGPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;//上拉GPIO_Init(GPIOE, &GPIO_InitStructure);//初始化GPIOE2,3,4
F103
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4|GPIO_Pin_3;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; //设置成上拉输入
GPIO_Init(GPIOE, &GPIO_InitStructure);//初始化GPIOE4,3
然后我们看一下不同点
首先F4的io的时钟挂载在AHB1下,调用
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOF, ENABLE);//使能GPIOF时钟其次
typedef struct
{uint32_t GPIO_Pin;GPIOMode_TypeDef GPIO_Mode;GPIOSpeed_TypeDef GPIO_Speed;GPIOOType_TypeDef GPIO_OType;//配置输出模式 GPIOPuPd_TypeDef GPIO_PuPd; //上下拉的配置
}GPIO_InitTypeDef;typedef enum
{ GPIO_PuPd_NOPULL = 0x00,GPIO_PuPd_UP = 0x01,GPIO_PuPd_DOWN = 0x02
}GPIOPuPd_TypeDef;f4能在GPIO_Init配置时就配置引脚的上下拉高低电平typedef enum
{ GPIO_OType_PP = 0x00,GPIO_OType_OD = 0x01
}GPIOOType_TypeDef;f4配置了输出后专门可以选择配置开漏输出和推挽输出,也就是比103分的更细一些了f4的速度最高可以配置为100mhz 103只有50mhz
NVIC
首先两者都要在main函数里面设置中断优先级的分组,且只设置一次设置了就别改了避免发生错误NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
F407
STM32F40xx/STM32F41xx 的 92 个中断里面,包括 10 个内核中断和 82 个可屏蔽中断,具
有 16 级可编程的中断优先级,而我们常用的就是这 82 个可屏蔽中断
F103
说了 CM3 内核支持 256 个中断,这里用 8 个 32 位寄存器来控制,每个位控制一个中断。但是
STM32F103 的可屏蔽中断只有 60 个
他们NVIC的配置是一样的
NVIC_InitTypeDef NVIC_InitStructure;
NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;//串口 1 中断
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=1 ;// 抢占优先级为 1
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2;// 子优先级位 2
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ 通道使能
NVIC_Init(&NVIC_InitStructure); //根据上面指定的参数初始化 NVIC 寄存器
外部中断
他们之间的中断服务函数都是一样的
EXTI0_IRQHandler
EXTI1_IRQHandler
EXTI2_IRQHandler
EXTI3_IRQHandler
EXTI4_IRQHandler
EXTI9_5_IRQHandler
EXTI15_10_IRQHandler
F407
STM32F407 的中断控制器支持 22
个外部中断/事件请求。每个中断设有状态位,每个中断/事件都有独立的触发和屏蔽设置。
STM32F407 的 22 个外部中断为:
EXTI 线 0~15:对应外部 IO 口的输入中断。
EXTI 线 16:连接到 PVD 输出。
EXTI 线 17:连接到 RTC 闹钟事件。
EXTI 线 18:连接到 USB OTG FS 唤醒事件。
EXTI 线 19:连接到以太网唤醒事件。
EXTI 线 20:连接到 USB OTG HS(在 FS 中配置)唤醒事件。
EXTI 线 21:连接到 RTC 入侵和时间戳事件。
EXTI 线 22:连接到 RTC 唤醒事件。STM32F4 供 IO 口使用的中断线只有 16 个
配置步骤
使能SYSCFG时钟: RCC_APB2PeriphClockCmd(RCC_APB2Periph_SYSCFG, ENABLE);
初始化IO口为输入。GPIO_Init();
设置IO口与中断线的映射关系。void SYSCFG_EXTILineConfig();
初始化线上中断,设置触发条件等。EXTI_Init();
配置中断分组(NVIC),并使能中断。NVIC_Init();
编写中断服务函数。EXTIx_IRQHandler();
清除中断标志位EXTI_ClearITPendingBit();
实例
一定要注意这里
RCC_APB2PeriphClockCmd(RCC_APB2Periph_SYSCFG, ENABLE);//使能SYSCFG时钟
SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOE, EXTI_PinSource3);//PE3 连接到中断线3
这两个是不一样的,F4是使能SYSCFG时钟来配置中断
NVIC_InitTypeDef NVIC_InitStructure;
EXTI_InitTypeDef EXTI_InitStructure;RCC_APB2PeriphClockCmd(RCC_APB2Periph_SYSCFG, ENABLE);//使能SYSCFG时钟
SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOE, EXTI_PinSource3);//PE3 连接到中断线3EXTI_InitStructure.EXTI_Line =EXTI_Line3EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;//中断事件EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling; //下降沿触发EXTI_InitStructure.EXTI_LineCmd = ENABLE;//中断线使能EXTI_Init(&EXTI_InitStructure);//配置NVIC_InitStructure.NVIC_IRQChannel = EXTI3_IRQn;//外部中断3NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x02;//抢占优先级2NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0x02;//子优先级2NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;//使能外部中断通道NVIC_Init(&NVIC_InitStructure);//配置
中断服务格式
void EXTI3_IRQHandler(void)
{
if(EXTI_GetITStatus(EXTI_Line3)!=RESET)//判断某个线上的中断是否发生
{ …中断逻辑…
EXTI_ClearITPendingBit(EXTI_Line3); //清除 LINE 上的中断标志位
}
}
F103
初始化IO口为输入。GPIO_Init();开启IO口复用时钟。RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);
设置IO口与中断线的映射关系。void GPIO_EXTILineConfig();
初始化线上中断,设置触发条件等。EXTI_Init();
配置中断分组(NVIC),并使能中断。NVIC_Init();
编写中断服务函数。EXTIx_IRQHandler();
清除中断标志位EXTI_ClearITPendingBit();
实例
io初始化省略
EXTI_InitTypeDef EXTI_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
//使能复用功能时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE); //配置 GPIO 与中断线的映射关系
//void GPIO_EXTILineConfig(uint8_t GPIO_PortSource, uint8_t GPIO_PinSource)GPIO_EXTILineConfig(GPIO_PortSourceGPIOE,GPIO_PinSource3);
将中断线 3 与 GPIOE 映射起来,那么很显然是 GPIOE.3 与 EXTI3 中断线连接了//初始化线上中断,设置触发条件等
EXTI_InitStructure.EXTI_Line=EXTI_Line3; //设置中断线的标号
EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt; //中断模式
//有两个模式中断和事件可选这里是中断
EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling;//设置触发方式
//设置触发方式,有上升沿和下降沿,还有双边沿,这里配置的是下降沿
EXTI_InitStructure.EXTI_LineCmd = ENABLE;
EXTI_Init(&EXTI_InitStructure); //根据EXTI_InitStruct中指定的参数初始化外设EXTI寄存器//配置中断分组(NVIC),并使能中断
NVIC_InitStructure.NVIC_IRQChannel = EXTI3_IRQn; //使能外部中断通道NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x02; //抢占优先级2 NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0x01; //子优先级1 NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //使能外部中断通道NVIC_Init(&NVIC_InitStructure); //根据NVIC_InitStruct中指定的参数初始化外设NVIC寄存器后面就是根据具体的要求写中断函数了
中断服务格式
void EXTI3_IRQHandler(void)
{
if(EXTI_GetITStatus(EXTI_Line3)!=RESET)//判断某个线上的中断是否发生
{
中断逻辑…
EXTI_ClearITPendingBit(EXTI_Line3); //清除 LINE 上的中断标志位
}
}
串口
串口也是很重要的一个环节。
F407
配置步骤
串口时钟使能:RCC_APBxPeriphClockCmd();GPIO时钟使能:RCC_AHB1PeriphClockCmd();
引脚复用映射:GPIO_PinAFConfig();
GPIO端口模式设置:GPIO_Init(); 模式设置为GPIO_Mode_AF
串口参数初始化:USART_Init();
开启中断并且初始化NVIC(如果需要开启中断才需要这个步骤)NVIC_Init();USART_ITConfig();
使能串口:USART_Cmd();
编写中断处理函数:USARTx_IRQHandler();
串口数据收发:
void USART_SendData();//发送数据到串口,DR
uint16_t USART_ReceiveData();//接受数据,从DR读取接受到的数据
串口传输状态获取:
FlagStatus USART_GetFlagStatus();
void USART_ClearITPendingBit();
实例
//GPIO端口设置GPIO_InitTypeDef GPIO_InitStructure;USART_InitTypeDef USART_InitStructure;NVIC_InitTypeDef NVIC_InitStructure;RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA,ENABLE); //使能GPIOA时钟RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE);//使能USART1时钟//串口1对应引脚复用映射GPIO_PinAFConfig(GPIOA,GPIO_PinSource9,GPIO_AF_USART1); //GPIOA9复用为USART1GPIO_PinAFConfig(GPIOA,GPIO_PinSource10,GPIO_AF_USART1); //GPIOA10复用为USART1//USART1端口配置GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9 | GPIO_Pin_10; //GPIOA9与GPIOA10GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;//复用功能GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //速度50MHzGPIO_InitStructure.GPIO_OType = GPIO_OType_PP; //推挽复用输出GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP; //上拉GPIO_Init(GPIOA,&GPIO_InitStructure); //初始化PA9,PA10//USART1 初始化设置USART_InitStructure.USART_BaudRate = bound;//波特率设置USART_InitStructure.USART_WordLength = USART_WordLength_8b;//字长为8位数据格式USART_InitStructure.USART_StopBits = USART_StopBits_1;//一个停止位USART_InitStructure.USART_Parity = USART_Parity_No;//无奇偶校验位USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;//无硬件数据流控制USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx; //收发模式USART_Init(USART1, &USART_InitStructure); //初始化串口1USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);//开启相关中断//Usart1 NVIC 配置NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;//串口1中断通道NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=3;//抢占优先级3NVIC_InitStructure.NVIC_IRQChannelSubPriority =3; //子优先级3NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道使能NVIC_Init(&NVIC_InitStructure); //根据指定的参数初始化VIC寄存器、USART_Cmd(USART1, ENABLE); //使能串口1 中断服务函数
F103
串口时钟使能,GPIO时钟使能:RCC_APB2PeriphClockCmd();
串口复位:USART_DeInit(); 这一步不是必须的
GPIO端口模式设置:GPIO_Init();
串口参数初始化:USART_Init();
开启中断并且初始化NVIC(如果需要开启中断才需要这个步骤)NVIC_Init();USART_ITConfig();
使能串口:USART_Cmd();
编写中断处理函数:USARTx_IRQHandler();
串口数据收发:
void USART_SendData();//发送数据到串口,DR
uint16_t USART_ReceiveData();//接受数据,从DR读取接受到的数据
串口传输状态获取:
FlagStatus USART_GetFlagStatus(USART_TypeDef* USARTx, uint16_t USART_FLAG);
void USART_ClearITPendingBit(USART_TypeDef* USARTx, uint16_t USART_IT);
实例
GPIO_InitTypeDef GPIO_InitStructure;
USART_InitTypeDef USART_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
//①串口时钟使能,GPIO 时钟使能,复用时钟使能
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1|
RCC_APB2Periph_GPIOA, ENABLE); //使能 USART1,GPIOA 时钟//②串口复位
USART_DeInit(USART1); //复位串口 1//③GPIO 端口模式设置
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9; //ISART1_TX PA.9
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //复用推挽输出
GPIO_Init(GPIOA, &GPIO_InitStructure); //初始化 GPIOA.9GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10; //USART1_RX PA.10
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; //浮空输入
GPIO_Init(GPIOA, &GPIO_InitStructure); //初始化 GPIOA.10//④串口参数初始化
USART_InitStructure.USART_BaudRate = 115200; //波特率设置
USART_InitStructure.USART_WordLength = USART_WordLength_8b; //字长为 8 位
USART_InitStructure.USART_StopBits = USART_StopBits_1; //一个停止位
USART_InitStructure.USART_Parity = USART_Parity_No; //无奇偶校验位
USART_InitStructure.USART_HardwareFlowControl
= USART_HardwareFlowControl_None; //无硬件数据流控制
USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;//收发模式
USART_Init(USART1, &USART_InitStructure); //⑤初始化 NVIC
NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=3 ; //抢占优先级 3
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3; //子优先级 3
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ 通道使能
NVIC_Init(&NVIC_InitStructure); //中断优先级初始化//⑤开启中断
USART_ITConfig(USART1, USART_IT_RXNE, ENABLE); //开启中断//⑥使能串口
USART_Cmd(USART1, ENABLE); //使能串口中断服务函数
不同点
1.首先都要开启usart的时钟和对应串口引脚的时钟,不同的是103串口的tx引脚配置的复用推挽输出,而rx配置的是浮空输入,而407rx,tx两个都配置的复用推完输出
2.407需要对串口对应的引脚设置引脚复用器映射:调用 GPIO_PinAFConfig 函数。
这两点大家一定要注意!
定时器
103的定时器资源
103的高级定时器是TIM1和TIM8
2,3,4,5是通用定时器
6,7是基本定时器
都是16位的
407的定时器资源
407的高级定时器是TIM1和TIM8
2,3,4,5,9,10,11,12,13,14是通用定时器
6,7是基本定时器
有16位和32位的
103和407的定时器中断
这个基本定时器的配置是一样的
能定时器时钟。RCC_APB1PeriphClockCmd();
初始化定时器,配置ARR,PSC。TIM_TimeBaseInit();
开启定时器中断,配置NVIC。void TIM_ITConfig();NVIC_Init();使能定时器。TIM_Cmd();
编写中断服务函数。TIMx_IRQHandler();
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;NVIC_InitTypeDef NVIC_InitStructure;RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE); //时钟使能//定时器TIM3初始化TIM_TimeBaseStructure.TIM_Period = arr; //设置自动重装载的值 TIM_TimeBaseStructure.TIM_Prescaler =psc; //设置用来作为TIMx时钟频率除数的预分频值TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1; //设置时钟分割:TDTS = Tck_timTIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; //TIM向上计数模式TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure); //根据指定的参数初始化TIMx的时间基数单位TIM_ITConfig(TIM3,TIM_IT_Update,ENABLE ); //使能指定的TIM3中断,允许更新中断//中断优先级NVIC设置NVIC_InitStructure.NVIC_IRQChannel = TIM3_IRQn; //TIM3中断NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0; //先占优先级0级NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3; //从优先级3级NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道被使能NVIC_Init(&NVIC_InitStructure); //初始化NVIC寄存器TIM_Cmd(TIM3, ENABLE); //使能TIMx //定时器3中断服务函数
void TIM3_IRQHandler(void)
{if(TIM_GetITStatus(TIM3,TIM_IT_Update)==SET) //溢出中断{LED1=!LED1;//DS1翻转}TIM_ClearITPendingBit(TIM3,TIM_IT_Update); //清除中断标志位
}
PWM
F407
使能定时器14和相关IO口时钟。使能定时器14时钟:RCC_APB1PeriphClockCmd();使能GPIOF时钟:RCC_AHB1PeriphClockCmd ();
初始化IO口为复用功能输出。函数:GPIO_Init();GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF; //复用功能GPIOF9复用映射到定时器14GPIO_PinAFConfig(GPIOF,GPIO_PinSource9,GPIO_AF_TIM14); 初始化定时器:ARR,PSC等:TIM_TimeBaseInit();初始化输出比较参数:TIM_OC1Init();使能预装载寄存器: TIM_OC1PreloadConfig(TIM14, TIM_OCPreload_Enable);
使能自动重装载的预装载寄存器允许位TIM_ARRPreloadConfig(TIM14,ENABLE);
使能定时器。 TIM_Cmd();
不断改变比较值CCRx,达到不同的占空比效果: TIM_SetCompare1();
GPIO_InitTypeDef GPIO_InitStructure;TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;TIM_OCInitTypeDef TIM_OCInitStructure;RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM14,ENABLE); //TIM14时钟使能 RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOF, ENABLE); //使能PORTF时钟 GPIO_PinAFConfig(GPIOF,GPIO_PinSource9,GPIO_AF_TIM14); //GPIOF9复用为定时器14GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9; //GPIOF9GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF; //复用功能GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz; //速度100MHzGPIO_InitStructure.GPIO_OType = GPIO_OType_PP; //推挽复用输出GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP; //上拉GPIO_Init(GPIOF,&GPIO_InitStructure); //初始化PF9TIM_TimeBaseStructure.TIM_Prescaler=psc; //定时器分频TIM_TimeBaseStructure.TIM_CounterMode=TIM_CounterMode_Up; //向上计数模式TIM_TimeBaseStructure.TIM_Period=arr; //自动重装载值TIM_TimeBaseStructure.TIM_ClockDivision=TIM_CKD_DIV1; TIM_TimeBaseInit(TIM14,&TIM_TimeBaseStructure);//初始化定时器14//初始化TIM14 Channel1 PWM模式 TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1; //选择定时器模式:TIM脉冲宽度调制模式2TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; //比较输出使能TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_Low; //输出极性:TIM输出比较极性低TIM_OC1Init(TIM14, &TIM_OCInitStructure); //根据T指定的参数初始化外设TIM1 4OC1TIM_OC1PreloadConfig(TIM14, TIM_OCPreload_Enable); //使能TIM14在CCR1上的预装载寄存器TIM_ARRPreloadConfig(TIM14,ENABLE);//ARPE使能 TIM_Cmd(TIM14, ENABLE); //使能TIM14
F103
使能定时器3和相关IO口时钟。使能定时器3时钟:RCC_APB1PeriphClockCmd();使能GPIOB时钟:RCC_APB2PeriphClockCmd();初始化IO口为复用功能输出。函数:GPIO_Init();GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
这里我们是要把PB5用作定时器的PWM输出引脚,所以要重映射配置,
所以需要开启AFIO时钟。同时设置重映射。RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);GPIO_PinRemapConfig(GPIO_PartialRemap_TIM3, ENABLE); 初始化定时器:ARR,PSC等:TIM_TimeBaseInit();初始化输出比较参数:TIM_OC2Init();
使能预装载寄存器: TIM_OC2PreloadConfig(TIM3, TIM_OCPreload_Enable);
使能定时器。TIM_Cmd();
不断改变比较值CCRx,达到不同的占空比效果:TIM_SetCompare2();
GPIO_InitTypeDef GPIO_InitStructure;TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;TIM_OCInitTypeDef TIM_OCInitStructure;RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE); //使能定时器3时钟RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB | RCC_APB2Periph_AFIO, ENABLE); //使能GPIO外设和AFIO复用功能模块时钟GPIO_PinRemapConfig(GPIO_PartialRemap_TIM3, ENABLE); //Timer3部分重映射 TIM3_CH2->PB5 //设置该引脚为复用输出功能,输出TIM3 CH2的PWM脉冲波形 GPIOB.5GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5; //TIM_CH2GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //复用推挽输出GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOB, &GPIO_InitStructure);//初始化GPIO//初始化TIM3TIM_TimeBaseStructure.TIM_Period = arr; //设置自动重装载值TIM_TimeBaseStructure.TIM_Prescaler =psc; //设置用来作为TIMx时钟频率除数的预分频值 TIM_TimeBaseStructure.TIM_ClockDivision = 0; //设置时钟分割:TDTS = Tck_timTIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; //TIM向上计数模式TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure); //根据TIM_TimeBaseInitStruct中指定的参数初始化TIMx的时间基数单位//初始化TIM3 Channel2 PWM模式 TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM2; //选择定时器模式:TIM脉冲宽度调制模式2TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; //比较输出使能TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High; //输出极性:TIM输出比较极性高TIM_OC2Init(TIM3, &TIM_OCInitStructure); //根据T指定的参数初始化外设TIM3 OC2TIM_OC2PreloadConfig(TIM3, TIM_OCPreload_Enable); //使能TIM3在CCR2上的预装载寄存器TIM_Cmd(TIM3, ENABLE); //使能TIM3
总结
如果移植之后发现不能用,首先检查IO的使能,如果使能没有问题进一步检查引脚配置的模式。该复用成对应功能的有没有复用。有用中断要注意F4中断时钟初始化的写法,如果用到SPI这种需要时序的,确认上面的没问题了,在检查一下时钟。
如果你的SPI发现自己动一动线就有数据了,这个时候就去看引脚的模式,是不是推挽输出。该浮空输入的有没有浮空输入,那个默认无上下拉的配置直接不要,不要写那个东西。
跳转链接
我这篇文章也是引用这位博主的文章,在此感谢。
相关文章:
如何快速移植(从STM32F103到STM32F407)
最近用到F4的地方比较多,网上代码还是F1多一些,便需要移植代码,如何快速移植代码呢? 看下面这篇文章 外设 首先就是STM32的外设了。 STM32F407ZGT6的基本外设 STM32F407ZGT6 作为 MCU,该芯片是 STM32F407 里面配置…...

python高级练习题库实验1(B)部分
文章目录 题目1代码实验结果题目2代码实验结果题目3代码实验结果题目4代码实验结果题目5代码实验结果题目总结题目1 打包糖果小游戏,用户输入糖果品牌与个数,还有一个盒子里面可以装多少个糖果,输出一些打印信息,如下图所示: 代码 print("Packaging lollies into…...
Qt Rsa 加解密方法使用(pkcs1, pkcs8, 以及文件存储和内存存储密钥)
Qt RSA 加解密 完整使用 密钥格式: pkcs#1pkcs#8 如何区分密钥对是PKCS1还是PKCS8? 通常PKCS1密钥对的开始部分为:-----BEGIN RSA PRIVATE KEY-----或 -----BEGIN RSA PUBLIC KEY-----。而PKCS8密钥对的开始部分为:-----BEGIN…...
区分物理端口与软件端口概念:以交换机端口和Linux系统中的端口为例
文章目录 交换机端口和Linux系统中的端口有什么区别?1. 交换机的端口2. Linux系统中的端口因此,尽管两者都被称为"端口",但它们代表的含义和用途是完全不同的。 交换机端口和Linux系统中的端口有什么区别? 虽然都被称为…...

力扣226:翻转二叉树
力扣226:翻转二叉树 给你一棵二叉树的根节点 root ,翻转这棵二叉树,并返回其根节点。 示例 1: 输入:root [4,2,7,1,3,6,9] 输出:[4,7,2,9,6,3,1] 示例 2: 输入:root [2,1,3]…...

亚马逊鲲鹏系统智能自动注册与AI角色养号,探索数字化新境界
在数字化时代,亚马逊鲲鹏系统以其强大的自动化功能,为用户提供了前所未有的购物体验。如果你想利用鲲鹏系统进行自动化注册,那么准备好邮箱、IP、手机号等关键信息后,你将轻松实现自动注册,为购物之旅开启智能化新篇章…...
AOP操作日志记录
AOP操作日志记录 1.创建注解 Retention(RetentionPolicy.RUNTIME) Target(ElementType.METHOD) public interface PassportLog {String operatePage();String operateType();ClassTypEnum classType();}2.创建切面 对于字典,可以通过注解属性去转换,枚举…...
Linux C语言 42-进程间通信IPC之网络通信(套接字)
Linux C语言 42-进程间通信IPC之网络通信(套接字) 本节关键字:C语言 进程间通信 网络通信 套接字 TCP UDP 相关库函数:socket、bind、listen、accept、send、recv、sendto、recvfrom 参考之前的文章 Linux C语言 30-套接字操作…...

微服务知识大杂烩
1.什么是微服务? 微服务(Microservices)是一种软件架构风格,将一个大型应用程序划分为一组小型、自治且松耦合的服务。每个微服务负责执行特定的业务功能,并通过轻量级通信机制(如HTTP)相互协作。每个微服务可以独立开发、部署和扩展,使得应用程序更加灵活、可伸缩和可…...

记录一次vscode markdown的图片路径相关插件学习配置过程
插件及说明查找过程 csdn搜索markdown图片路径,找到关于这一款插件的回答。打开vscode拓展搜索Paste Image这款插件,看到下载量挺高的,应该不赖。 点击仓库,进入该插件开源的github仓库,查看README文件阅读说明. 淡然在Vscode 插件项目下的细…...

设计原则 | 依赖转置原则
一、依赖转置原则(DIP:Dependence Inversion Principle) 1、原理 高层模块不应该依赖低层模块,二者都应该依赖于抽象抽象不应该依赖于细节,细节应该依赖于抽象 2、层次化 Booch曾经说过:所有结构良好的面…...
前端开发实用技巧与经验分享
导语:在前端开发领域,掌握一些实用的技巧和经验可以帮助你更高效地完成任务。本文将分享一些前端开发的实用技巧和经验,帮助你在工作中更好地应对各种挑战。 一、使用开发者工具进行调试和优化 熟练掌握浏览器开发者工具的使用,…...

推荐一款Excel快速加载SQL的插件,方便又好用
如果告诉你只需要双击一下,SQL数据库中存放在表里面的数据,就能加载到你的Excel中,你想不想要? 今天给大家推荐一款好用的Excel插件,安装简单,使用方便,是经常使用SQL数据库的不二。 这款插件…...

Docker快速入门(docker加速,镜像,容器,数据卷常见命令操作整理)
Docker本质是将代码所需的环境依赖进行打包运行,而在Docker中最重要的是镜像和容器 镜像:可以简单地理解为每启动一个docker镜像就会占用计算机一个进程,这个进程和另外起的docker镜像的进程是相互独立的,以数据库为例,每个镜像都会copy一份数据库,在他所在的进程中.别的镜像在…...

http和https的区别有哪些
目录 HTTP(HyperText Transfer Protocol) HTTPS(HyperText Transfer Protocol Secure) 区别与优势 应用场景 未来趋势 当我们浏览互联网时,我们经常听到两个常用的协议:HTTP(HyperText Tra…...

使用Keil-MDK生成*.bin格式可执行文件
使用Keil-MDK生成*.bin格式可执行文件 文章目录 使用Keil-MDK生成*.bin格式可执行文件前言一、fromelf.exe工具二、使用方法1.配置输出2.输出格式 前言 在使用Keil MDK的集成开发环境中,默认情况下可以生成*.axf格式的调试文件和*.hex格式的可执行文件。虽然文件可…...

基于springboot+vue篮球联盟管理系统源码
🍅 简介:500精品计算机源码学习 🍅 欢迎点赞 👍 收藏 ⭐留言 📝 文末获取源码 目录 一、以下学习内容欢迎交流: 二、文档资料截图: 三、项目技术栈 四、项目运行图 背景: 篮球运…...

分页助手入门以及小bug,报sql语法错误
导入坐标 5版本以上的分页助手 可以不用手动指定数据库语言,它会自动识别 <dependency> <groupId>com.github.pagehelper</groupId> <artifactId>pagehelper</artifactId> <version>5.3.2</version> </dependency&g…...
Java中的并发编程:深入理解CountDownLatch
Java中的并发编程:深入理解CountDownLatch 本文将深入探讨Java中的并发编程,重点关注CountDownLatch的使用。通过理解这些概念和技术,我们可以编写出更高效、稳定的Java程序。 一、CountDownLatch简介 CountDownLatch是Java中的一个同步工具…...
Windows 安装 flash-attention 和 bitsandbytes
首先保证cuda版本为12.1,torch版本为2.1.0及以上,python版本3.10以上 从此处下载最新版的whl,https://github.com/jllllll/bitsandbytes-windows-webui/releases/tag/wheels,通过whl来安装bitsandbytes 从此处下载最新版的whl&a…...
Python|GIF 解析与构建(5):手搓截屏和帧率控制
目录 Python|GIF 解析与构建(5):手搓截屏和帧率控制 一、引言 二、技术实现:手搓截屏模块 2.1 核心原理 2.2 代码解析:ScreenshotData类 2.2.1 截图函数:capture_screen 三、技术实现&…...
云计算——弹性云计算器(ECS)
弹性云服务器:ECS 概述 云计算重构了ICT系统,云计算平台厂商推出使得厂家能够主要关注应用管理而非平台管理的云平台,包含如下主要概念。 ECS(Elastic Cloud Server):即弹性云服务器,是云计算…...

2.Vue编写一个app
1.src中重要的组成 1.1main.ts // 引入createApp用于创建应用 import { createApp } from "vue"; // 引用App根组件 import App from ./App.vue;createApp(App).mount(#app)1.2 App.vue 其中要写三种标签 <template> <!--html--> </template>…...

React19源码系列之 事件插件系统
事件类别 事件类型 定义 文档 Event Event 接口表示在 EventTarget 上出现的事件。 Event - Web API | MDN UIEvent UIEvent 接口表示简单的用户界面事件。 UIEvent - Web API | MDN KeyboardEvent KeyboardEvent 对象描述了用户与键盘的交互。 KeyboardEvent - Web…...

DBAPI如何优雅的获取单条数据
API如何优雅的获取单条数据 案例一 对于查询类API,查询的是单条数据,比如根据主键ID查询用户信息,sql如下: select id, name, age from user where id #{id}API默认返回的数据格式是多条的,如下: {&qu…...

涂鸦T5AI手搓语音、emoji、otto机器人从入门到实战
“🤖手搓TuyaAI语音指令 😍秒变表情包大师,让萌系Otto机器人🔥玩出智能新花样!开整!” 🤖 Otto机器人 → 直接点明主体 手搓TuyaAI语音 → 强调 自主编程/自定义 语音控制(TuyaAI…...
Java求职者面试指南:计算机基础与源码原理深度解析
Java求职者面试指南:计算机基础与源码原理深度解析 第一轮提问:基础概念问题 1. 请解释什么是进程和线程的区别? 面试官:进程是程序的一次执行过程,是系统进行资源分配和调度的基本单位;而线程是进程中的…...

RabbitMQ入门4.1.0版本(基于java、SpringBoot操作)
RabbitMQ 一、RabbitMQ概述 RabbitMQ RabbitMQ最初由LShift和CohesiveFT于2007年开发,后来由Pivotal Software Inc.(现为VMware子公司)接管。RabbitMQ 是一个开源的消息代理和队列服务器,用 Erlang 语言编写。广泛应用于各种分布…...

Selenium常用函数介绍
目录 一,元素定位 1.1 cssSeector 1.2 xpath 二,操作测试对象 三,窗口 3.1 案例 3.2 窗口切换 3.3 窗口大小 3.4 屏幕截图 3.5 关闭窗口 四,弹窗 五,等待 六,导航 七,文件上传 …...
uniapp 集成腾讯云 IM 富媒体消息(地理位置/文件)
UniApp 集成腾讯云 IM 富媒体消息全攻略(地理位置/文件) 一、功能实现原理 腾讯云 IM 通过 消息扩展机制 支持富媒体类型,核心实现方式: 标准消息类型:直接使用 SDK 内置类型(文件、图片等)自…...