知识点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(复用推挽输出)
结束
代码重在练习!
代码重在练习!
代码重在练习!
今天的分享就到此结束了,希望对你有所帮助。如果你喜欢我的分享,请点赞收藏加关注,谢谢大家!!!