目录
GPIO
名称 | 功能 |
GPIO_PIN_RESET |
低电平(0) |
GPIO_PIN_SET |
高电平(1) |
定时器
us延时函数实现:
HAL库:
/**************************************滴答定时器延时**********************************/
static uint16_t g_fac_us = 0; /* us延时倍乘数 */
/**
* @brief 初始化延迟函数
* @param sysclk: 系统时钟频率, 即CPU频率(HCLK)
* @retval 无
*/
void delay_init(uint16_t sysclk) //例如:g_fac_us = 72 / 8
{
SysTick->CTRL = 0; /* 清Systick状态,以便下一步重设,如果这里开了中断会关闭其中断 */
HAL_SYSTICK_CLKSourceConfig(SYSTICK_CLKSOURCE_HCLK_DIV8); /* SYSTICK使用内核时钟源8分频,因systick的计数器最大值只有2^24 */
g_fac_us = sysclk / 8; /* g_fac_us作为1us的基础时基 */
}
/**
* @brief 延时nus
* @param nus: 要延时的us数.
* @note 注意: nus的值,不要大于1864135us(最大值即2^24 / g_fac_us @g_fac_us = 9)
* @retval 无
*/
void delay_us(uint32_t nus)
{
uint32_t temp;
SysTick->LOAD = nus * g_fac_us; /* 时间加载 */
SysTick->VAL = 0x00; /* 清空计数器 */
SysTick->CTRL |= 1 << 0 ; /* 开始倒数 */
do
{
temp = SysTick->CTRL;
} while ((temp & 0x01) && !(temp & (1 << 16))); /*判断temp的最低位(即CTRL.ENABLE位)是否为1,表示SysTick定时器使能;同时,判断temp的第16位是否为0,表示时间还未到达。*/
SysTick->CTRL &= ~(1 << 0) ; /* 关闭SYSTICK */
SysTick->VAL = 0X00; /* 清空计数器 */
}
/**
* @brief 延时nms
* @param nms: 要延时的ms数 (0< nms <= 65535)
* @retval 无
*/
void delay_ms(uint16_t nms)
{
uint32_t repeat = nms / 1000; /* 记录超出1000ms的值,即1s */
uint32_t remain = nms % 1000; /* 记录未超出1000ms的值 */
while (repeat)
{
delay_us(1000 * 1000); /* 利用delay_us 实现 1000ms 延时 */
repeat--;
}
if (remain)
{
delay_us(remain * 1000); /* 利用delay_us, 把尾数延时(remain ms)给做了 */
}
}
/*******************************************定时器延时 1************************/
// 初始化定时器
void TIM_Init(void)
{
TIM_HandleTypeDef htim;
htim.Instance = TIM2;
htim.Init.Period = 0xFFFF;
htim.Init.Prescaler = HAL_RCC_GetPCLK1Freq() / 1000000 - 1; // 设置定时器时钟为1MHz
htim.Init.ClockDivision = 0;
htim.Init.CounterMode = TIM_COUNTERMODE_UP;
HAL_TIM_Base_Init(&htim);
}
// 微秒级延时函数
void Delay_us(uint32_t us)
{
__HAL_TIM_SET_COUNTER(&htim2, 0); // 清空定时器计数器
HAL_TIM_Base_Start(&htim2); // 启动定时器
while (__HAL_TIM_GET_COUNTER(&htim2) < us); // 等待定时器计数器达到指定值
HAL_TIM_Base_Stop(&htim2); // 停止定时器
}
标准库:
/********************************滴答定时器延时************************************/
static u8 fac_us = 0;
static u16 fac_ms = 0;
void delay_init(void)
{
SysTick_CLKSourceConfig(SysTick_CLKSource_HCLK_Div8); // 选择外部时钟 HCLK/8
fac_us = SystemCoreClock / 8000000;
fac_ms = (u16)fac_us * 1000;
}
void delay_us(u32 nus)
{
u32 temp;
SysTick->LOAD = nus * fac_us; // 延时时间加载
SysTick->VAL = 0x00; // 清空计数器
SysTick->CTRL |= SysTick_CTRL_ENABLE_Msk; // 开始倒数
do
{
temp = SysTick->CTRL;
} while ((temp & 0x01) && !(temp & (1 << 16))); // 等待时间到达
SysTick->CTRL &= ~SysTick_CTRL_ENABLE_Msk; // 关闭计数器
SysTick->VAL = 0x00; // 清空计数器
}
void delay_ms(u16 nms)
{
u32 temp;
SysTick->LOAD = (u32)nms * fac_ms; // 时间加载(SysTick->LOAD为24bit)
SysTick->VAL = 0x00; // 清空计数器
SysTick->CTRL |= SysTick_CTRL_ENABLE_Msk; // 开始倒数
do
{
temp = SysTick->CTRL;
} while ((temp & 0x01) && !(temp & (1 << 16))); // 等待时间到达
SysTick->CTRL &= ~SysTick_CTRL_ENABLE_Msk; // 关闭计数器
SysTick->VAL = 0x00; // 清空计数器
}
/*******************************************定时器延时 1************************/
void TIM_Init(void)
{
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE); // Enable the clock for TIM2
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
TIM_TimeBaseStructure.TIM_Period = 0xFFFF;
TIM_TimeBaseStructure.TIM_Prescaler = SystemCoreClock / 1000000 - 1; // Set timer clock to 1MHz
TIM_TimeBaseStructure.TIM_ClockDivision = 0;
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure);
TIM_Cmd(TIM2, ENABLE); // Enable TIM2
}
void Delay_us(uint32_t us)
{
TIM_SetCounter(TIM2, 0); // Clear the timer counter
while (TIM_GetCounter(TIM2) < us); // Wait until the timer counter reaches the specified value
}
/*******************************************定时器延时 2************************/
/*最精确*/
/**
开启一个毫秒级定时器,等待时间到将flag复位
*/
volatile unsigned int delayFlag = 0;
/*
us微妙级延时定时器初始化
*/
void DelayInit(void)
{
/* -----------------------------------------------------------------------
系统主频120MHZ,timer_initpara.prescaler为11,timer_initpara.period为9,频率就为1MHZ
----------------------------------------------------------------------- */
timer_parameter_struct timer_initpara;
rcu_periph_clock_enable(RCU_TIMER1);
timer_deinit(TIMER1);
/* TIMER1 configuration */
timer_initpara.prescaler = 11;
timer_initpara.period = 9;
timer_initpara.alignedmode = TIMER_COUNTER_EDGE;
timer_initpara.counterdirection = TIMER_COUNTER_UP;
timer_initpara.clockdivision = TIMER_CKDIV_DIV1;
timer_initpara.repetitioncounter = 0;
timer_init(TIMER1,&timer_initpara);
nvic_priority_group_set(NVIC_PRIGROUP_PRE2_SUB2);
nvic_irq_enable(TIMER1_IRQn, 0, 1);
timer_interrupt_enable(TIMER1, TIMER_INT_UP);
/* auto-reload preload enable */
timer_auto_reload_shadow_enable(TIMER1);
/* auto-reload preload enable */
timer_enable(TIMER1);
}
void Delay_ms(u16 ms)
{
delayFlag = ms * 1000;
while(delayFlag);
}
void Delay_us(u32 nus)
{
delayFlag = nus;
while(delayFlag);
}
void TIMER1_IRQHandler(void)
{
timer_flag_clear(TIMER1,TIMER_FLAG_UP);
if (0U != delayFlag){
delayFlag--;
}
}
标准库:
需要使能TIM_Cmd(TIM8, ENABLE); 和 TIM3_IRQHandler中断服务函数
HAL库:
要手动开启和关闭定时器及其中断:
HAL_StatusTypeDef HAL_TIM_Base_Stop(TIM_HandleTypeDef *htim)
HAL_StatusTypeDef HAL_TIM_Base_Stop(TIM_HandleTypeDef *htim)
HAL_StatusTypeDef HAL_TIM_Base_Start_IT(TIM_HandleTypeDef *htim)
HAL_StatusTypeDef HAL_TIM_Base_Stop_IT(TIM_HandleTypeDef *htim)
中断回调函数 -- 名字不能错误 && 一定要先开定时器中断,再开定时器
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
if (htim == &htimx)
{
}
}
PWM输出通道
脉冲宽度可调制。 -- 使用地方:电机调速(直流电机、步进电机)、舵机、灯的亮度等等。 占空比:高电平 所占整个周期的比例
当计数器值与比较寄存器值相等时,电平发生反转。
有效电平自己设置: 假设:有效电平 = 高电平
PWM1模式:计数器小于比较寄存器,输出有效电平
PWM2模式:计数器小于比较寄存器,输出无效电平
IO模拟
//固定占空比(占空比可变) 放在中断中执行设置频率
#define PWM_PERIOD 1000 // PWM周期(单位:循环次数)
#define PWM_DUTY_CYCLE 500 // PWM占空比50%(单位:循环次数)
// 函数声明
void LED_Init(void); // 初始化LED引脚
void LED_Write(bool state); // 写入LED引脚状态
int main(void) {
LED_Init(); // 初始化LED引脚
while (1) {
for (uint16_t i = 0; i < PWM_PERIOD; i++) {
if (i < PWM_DUTY_CYCLE) {
LED_Write(true); // LED引脚输出高电平
} else {
LED_Write(false); // LED引脚输出低电平
}
}
}
}
void LED_Init(void) {
// 初始化LED引脚为输出模式
// 这里的实现取决于您的硬件平台
}
void LED_Write(bool state) {
if (state) {
// 将LED引脚设置为高电平
// 这里的实现取决于您的硬件平台
} else {
// 将LED引脚设置为低电平
// 这里的实现取决于您的硬件平台
}
}
单路输出
标准库:
开启 TIM_CtrlPWMOutputs(TIM8, ENABLE);
设置比较值 TIM_SetCompare1(TIM8, pwm);
HAL库:
开启
HAL_StatusTypeDef HAL_TIM_PWM_Start(TIM_HandleTypeDef *htim, uint32_t Channel)
设置比较值(设置占空比)
__HAL_TIM_SET_COMPARE(__HANDLE__, __CHANNEL__, __COMPARE__)
互补输出
高级定时器特有,电机控制常用。分频和单路相同
DMA模式
开启DMA
HAL_StatusTypeDef HAL_TIM_PWM_Start_DMA(TIM_HandleTypeDef *htim, uint32_t Channel, uint32_t *pData, uint16_t Length)
关闭DMA
DMA中断里调用,不然可能出现少了脉冲
HAL_StatusTypeDef HAL_TIM_PWM_Stop_DMA(TIM_HandleTypeDef *htim, uint32_t Channel)
PWM+DMA中断回调函数
void HAL_TIM_PWM_PulseFinishedCallback(TIM_HandleTypeDef *htim)
{
if (htim == &htimx)
{
}
}
从内存到外设 普通模式 建议设置为字(word)模式
中断
NVIC_PriorityGroup_4 0-15级抢占优先级 0级子优先级
4bit全用于抢占优先级 -- 0bit用于子优先级
中断优先级与任务优先级区别
没有关系。中断的优先级永远高于任何任务的优先级,即任务在执行的过程中,中断来了就开始执行中断服务程序。