STM32核心机制解析:重映射、时间片与系统定时器实战——从理论到呼吸灯开发

发布于:2025-05-13 ⋅ 阅读:(16) ⋅ 点赞:(0)

知识点1【重映射的概念】

1、引入

默认:

**定义:**系统或硬件在未经用户修改时的预设配置或行为

STM32的引脚功能、外设配置、中断向量表等默认由芯片设计或库函数预设。

部分重映射

**定义:**仅修改部分资源或地址的映射关系,其余保留默认配置

通过AFIO(Alternate Function I/O)或外设寄存器,仅修改部分外设的引脚映射,其他功能保持默认。

完全重映射

**定义:**彻底替换原有映射关系,重新分配所有资源或地址的映射规则。

彻底改变外设或系统资源的全局映射关系,通常需要重新配置硬件或系统级寄存器

注意:影响全局功能,谨慎操作。

2、定时器的映射关系图

知识点2【GPIO和AFIO】

1、GPIO

主要用于最基础的数字信号输入/输出

输入模式:浮空,上拉,下拉,模拟

输出模式:推挽,开漏或复用功能

2、AFIO

专门管理”外设功能“如何映射到特定引脚。

比如USART1,SPI1,I2C1,定时器PWM,外部中断线(EXTI)等外设,都有多个候选引脚,AFIO通过重映射(Remap)寄存器决定最终用哪个引脚。

知识点3【复用推挽/开漏 与 推挽/开漏】

注意:

用到重映射想到 对应端口需要 复用

用到外设要想到 对应端口需要 复用

用到外部中断想到 对应端口需要 复用

知识点4【时间片】

**时间片(Time Slice)**是操作系统中 CPU时间分配的最小单元

**核心思想:**将CPU的执行时间划分称多个固定长度的时间片段,每个任务(或线程/进程)在一个时间片内独占CPU,时间片用完后强制切换其他任务。

调度流程:

  • 步骤 1:任务 A 获得 CPU 使用权,开始执行。
  • 步骤 2:系统时钟中断触发,检查已用时间片。
  • 步骤 3:若时间片耗尽,将任务 A 挂起,切换到任务 B。
  • 步骤 4:任务 A 重新等待调度,进入就绪队列。

时间片主要是为了实现非阻塞

知识点5【系统定时器】

系统定时器又称 嘀嗒定时器,属于内核部分。

系统定时器中采用向下计数没有预分频器,有计数器,与 重装载计数器

计数器:计数范围0-FFFFFF,24位

重装载计数器:计数范围0-FFFFFF,24位

系统定时器一般用于实现延时,下面我写一段延时代码,主要看逻辑,函数无需刻意记忆,用多了自然会记住,记不住可以看手册。

函数介绍 SysTick_Config

static __INLINE uint32_t SysTick_Config(uint32_t ticks)

函数功能:

是ARM Cortex-M内核中SysTick定时器的初始化函数,用来配置SysTick定时器的 中断周期

主要是设置SysTick定时器的重装载值(Reload Value),并启动定时器和中断、

参数:

ticks:SysTick定时器的重装载值,决定中断触发周期

返回值:

0:配置成功

-1:配置失败(ticks超出SysTick寄存器的最大范围)

触发中断对应的中断服务函数

void SysTick_Handler(void)

代码演示

#include "stm32f10x.h" 
#include "stm32f10x_conf.h"

void Systick_Init(u32 ticks);
void Delay_ms(u32 ms);
void SysTick_Handler(void);
void LED_Init(void);
void LED0_Blink(u32 ms);
void LED1_Blink(u32 ms);
	
u32 delay_ms = 0;

//这里我们实现一个两个灯独立闪烁,互不影响
//LED0:闪烁周期0.4s  PB5
//LED1:闪烁周期0.6s  PE5

u32 LED0[2] = {0,0};
u32 LED1[2] = {0,0};

int main(void)
{
	//打开系统定时器
	Systick_Init(72000);//每隔1ms delay_ms + 1
	LED_Init();
	while(1)
	{
		LED0_Blink(400);
		LED1_Blink(600);
	}
	
}

//系统定时器初始化函数
void Systick_Init(u32 ticks)
{
	SysTick_Config(ticks);
}

void LED_Init(void)
{
//	GPIO_InitTypeDef GPIOB_InitStruct;
	GPIO_InitTypeDef GPIOE_InitStruct;
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOE,ENABLE);
	
//	GPIOB_InitStruct.GPIO_Mode = GPIO_Mode_Out_PP;
//	GPIOB_InitStruct.GPIO_Pin = GPIO_Pin_5;
//	GPIOB_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
	
	GPIOE_InitStruct.GPIO_Mode = GPIO_Mode_Out_PP;
	GPIOE_InitStruct.GPIO_Pin = GPIO_Pin_5;
	GPIOE_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
	
	GPIO_Init(GPIOB,&GPIOE_InitStruct);
	GPIO_Init(GPIOE,&GPIOE_InitStruct);
	
	GPIO_SetBits(GPIOB,GPIO_Pin_5);
	GPIO_SetBits(GPIOE,GPIO_Pin_5);
}

//延时函数
void Delay_ms(u32 ms)
{
	u32 ticks = delay_ms + ms;
	while(ticks > delay_ms);
}

void LED0_Blink(u32 ms)
{
	LED0[1] = ms;
	if(LED0[0] >= LED0[1])
	{
		GPIO_WriteBit(GPIOB,GPIO_Pin_5,(BitAction)(1 - GPIO_ReadOutputDataBit(GPIOB,GPIO_Pin_5)));
		LED0[0] = 0;
	}
}
void LED1_Blink(u32 ms)
{
	LED1[1] = ms;
	if(LED1[0] >= LED1[1])
	{
		GPIO_WriteBit(GPIOE,GPIO_Pin_5,(BitAction)(1 - GPIO_ReadOutputDataBit(GPIOE,GPIO_Pin_5)));
		LED1[0] = 0;
	}
}

//系统定时器中断服务函数
void SysTick_Handler(void)
{
	delay_ms++;
	LED1[0]++;
	LED0[0]++;
}

代码遇到的问题

SysTick_Handler重定义

解决方法

屏蔽掉即可

知识点6【定时器常用标准库函数】

详细函数使用看手册

注意事项:

TIM_GetITStatus与TIM_ClearITPendingBit配套使用

TIM_GetFlagStatus 与TIM_ClearFlag 配套使用

代码演示

这里我们使用标准库 实现了使用 OC(输出比较)的中断,来控制CCR寄存器中的值,进而 实现呼吸灯

#include "stm32f10x.h" 
#include <stm32f10x_tim.h>
#include <stm32f10x_conf.h>
#include "delay.h"
//定时器的OC(PA1  定时器2通道2)中断实现LED(PB5)呼吸灯
void LED_Init(void);
void TIM3_CH2_Init(void);

int dirction = 1;
int num_ccr = 0;

int main(void)
{
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
	
	//开启AFIO  重映射之前需要开启
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);
	//重映射
	GPIO_PinRemapConfig(GPIO_PartialRemap_TIM3,ENABLE);
	
	LED_Init();
	TIM3_CH2_Init();
	while(1)
	{
		
	}
}
//LED初始化 PB5
void LED_Init(void)
{
	GPIO_InitTypeDef GPIOB_InitStruct;
	
	//时钟配置
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);
	
	//管脚输出模式配置
	GPIO_DeInit(GPIOB);
	
	GPIOB_InitStruct.GPIO_Mode = GPIO_Mode_AF_PP;
	GPIOB_InitStruct.GPIO_Pin = GPIO_Pin_5;
	GPIOB_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOB,&GPIOB_InitStruct);
	
	//熄灭
	GPIO_SetBits(GPIOB,GPIO_Pin_5);
}

//TIM3初始化 通道2 PA2
void TIM3_CH2_Init(void)
{
	TIM_TimeBaseInitTypeDef TIM3_TimeBaseInitStruct;
	TIM_OCInitTypeDef TIM3_OC2InitStruct;
	NVIC_InitTypeDef NVIC_InitStruct_TIM3_CH2;
	
	//时钟配置
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3,ENABLE);
	
	//定时器 时基单元 配置
	TIM_TimeBaseStructInit(&TIM3_TimeBaseInitStruct);
	
	TIM3_TimeBaseInitStruct.TIM_ClockDivision = TIM_CKD_DIV1;
	TIM3_TimeBaseInitStruct.TIM_CounterMode = TIM_CounterMode_Up;
	TIM3_TimeBaseInitStruct.TIM_Period = 1000 - 1;
	TIM3_TimeBaseInitStruct.TIM_Prescaler = 72 - 1;
	TIM_TimeBaseInit(TIM3,&TIM3_TimeBaseInitStruct);
	
	//OC配置
	TIM_OCStructInit(&TIM3_OC2InitStruct);
	TIM3_OC2InitStruct.TIM_OCMode = TIM_OCMode_PWM1;
	TIM3_OC2InitStruct.TIM_Pulse = 0;	
	TIM3_OC2InitStruct.TIM_OCPolarity = TIM_OCPolarity_Low;
	TIM3_OC2InitStruct.TIM_OutputState = TIM_OutputState_Enable;
	TIM_OC2Init(TIM3,&TIM3_OC2InitStruct);
	
	//输出比较使能
	//TIM_OC2PreloadConfig(TIM3, TIM_OCPreload_Enable);
	
	//中断使能
	TIM_ITConfig(TIM3,TIM_IT_CC2,ENABLE);
	
	//NVIC配置
	NVIC_InitStruct_TIM3_CH2.NVIC_IRQChannel = TIM3_IRQn;
	NVIC_InitStruct_TIM3_CH2.NVIC_IRQChannelCmd = ENABLE;
	NVIC_InitStruct_TIM3_CH2.NVIC_IRQChannelPreemptionPriority = 0x01;
	NVIC_InitStruct_TIM3_CH2.NVIC_IRQChannelSubPriority = 0x01;
	NVIC_Init(&NVIC_InitStruct_TIM3_CH2);
	
	//启动定时器
	TIM_Cmd(TIM3,ENABLE);
}

//中断服务函数
void TIM3_IRQHandler(void)
{
	if(TIM_GetITStatus(TIM3,TIM_IT_CC2) == SET)
	{
		
			//条件判断 
		//CCR == 1000-》DIR = -1;
		//CCR == 0-》DIR = 1;
		if(num_ccr <= 0)
		{
			num_ccr = 0;
			dirction = 1;
		}
		else if(num_ccr >= 1000 - 1)
		{
			num_ccr = 1000 - 1;
			dirction = -1;
		}
		num_ccr += dirction;
		TIM_SetCompare2(TIM3,num_ccr);
		
		//清除中断标志位
		TIM_ClearITPendingBit(TIM3, TIM_IT_CC2);
	}
}

代码犯错:

1、输出比较寄存器需要使能,在对应的结构体内使能

TIM3_OC2InitStruct.TIM_OutputState = TIM_OutputState_Enable;

2、使用了 重映射PB5,输出模式要用AF_PP(复用推挽输出)

结束

代码重在练习!

代码重在练习!

代码重在练习!

今天的分享就到此结束了,希望对你有所帮助。如果你喜欢我的分享,请点赞收藏加关注,谢谢大家!!!


网站公告

今日签到

点亮在社区的每一天
去签到