沁恒CH582M开发板-3-Systick滴答定时器的使用

本文介绍了在沁恒CH582M开发板上使用Systick滴答定时器的方法。通过库函数封装,实现简单LED灯交替闪烁的示例代码。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

3-Systick滴答定时器的使用

滴答定时器的使用非常简单,沁恒已经为我们封装好了现成的库函数:

RV_STATIC_INLINE uint32_t SysTick_Config(uint64_t ticks)
{
   
   
    if((ticks - 1) > SysTick_LOAD_RELOAD_Msk)
        return (1); /* Reload value impossible */

    SysTick->CMP = ticks - 1; /* set reload register */
    PFIC_EnableIRQ
<think>我们需要在STM32上实现一个通过串口接收命令控制RGB LED的呼吸灯效果,并且能够实时切换颜色。 要求: 1. 定义三个十六进制命令,例如:0xAA 0xBB 0xCC(具体命令可以根据需要定义),分别对应不同的颜色切换(比如红、绿、蓝)。 2. 实现呼吸灯效果,即LED的亮度逐渐变化(通常使用PWM实现)。 3. 能够通过串口命令实时切换颜色。 思路: - 使用一个定时器产生PWM波,控制RGB三个通道的亮度。 - 使用串口中断接收命令,当接收到特定命令时,改变当前呼吸灯的目标颜色。 - 呼吸灯效果可以通过改变PWM占空比来实现,通常使用线性增加然后线性减少的方式(类似三角波),或者使用正弦波等方式。这里我们使用简单的三角波方式。 - 由于有三个颜色通道,我们可能需要为每个通道设置独立的PWM值。但是为了简化,我们可以假设只有一个LED灯,它可以显示不同的颜色,并且颜色切换是整体切换(比如切换到红色时,只有红色通道亮,其他通道灭)。但为了效果,我们可以实现混合颜色,但题目要求是切换颜色,所以我们可以设定为单色呼吸灯,然后通过命令切换不同颜色。 具体步骤: 1. 初始化系统时钟,包括定时器(用于PWM)和串口。 2. 配置定时器为PWM模式,有三个通道分别对应R、G、B三个LED引脚。 3. 配置串口,使能接收中断。 4. 在串口中断服务函数中,判断接收到的命令(三个字节的命令),根据命令设置目标颜色(例如,0xAA命令设置为红色,0xBB为绿色,0xCC为蓝色)。 5. 在主循环中,实现呼吸灯效果:循环改变当前颜色的PWM占空比(从0到最大值,然后从最大值到0),在改变过程中,如果接收到新的命令,则切换到新的颜色。 注意:呼吸灯效果需要平滑变化,因此占空比的变化应该是连续的。我们可以使用一个全局变量表示当前占空比,并设定一个步进值,每次定时器中断(或者使用另一个定时器)更新占空比。但是为了简化,我们可以使用一个定时器同时产生PWM和更新占空比(使用定时器中断更新占空比,然后改变PWM的占空比寄存器)。 然而,更常见的做法是使用一个定时器产生PWM,然后使用另一个定时器或者系统滴答定时器SysTick)来更新占空比的值,实现呼吸效果。 这里我们采用: - 使用一个定时器(TIM1或TIM2等)的三个通道输出PWM,分别控制RGB。 - 使用SysTick定时器中断来更新呼吸灯的占空比(即改变PWM的占空比)。 具体实现: - 设置一个方向标志(direction),当占空比增加到最大值时,方向变为减;当减少到0时,方向变为增。 - 设置一个颜色状态变量(current_color),表示当前显示的颜色(比如0表示红色,1表示绿色,2表示蓝色)。在更新占空比的时候,只改变当前颜色的那个通道的占空比,其他通道置0。 - 当接收到串口命令时,改变current_color,并且重置呼吸灯状态(比如占空比从0开始增加)。 但是,注意:在切换颜色时,我们希望平滑过渡,而不是立即切换。但题目要求是实时切换,我们可以立即切换到新的颜色,然后从当前占空比开始继续呼吸(或者重置为0开始)。这里我们选择切换时重置为0开始。 另一种更平滑的方式是:在切换颜色时,将当前颜色的占空比逐渐降低,同时将新颜色的占空比逐渐升高(类似于交叉淡入淡出)。但题目没有要求,我们按照简单方式:立即切换,并重置呼吸状态。 步骤: 1. 初始化:系统时钟、GPIO(PWM输出引脚)、定时器(PWM)、串口、SysTick定时器(用于更新占空比)。 2. 定义全局变量: - 当前颜色(current_color) - 当前占空比(duty_cycle) - 方向(direction,1表示增加,0表示减少) - 呼吸灯的步长(step) - 最大占空比(max_duty,一般等于定时器的自动重装载值ARR) 3. 在SysTick中断中,根据方向更新duty_cycle,并设置到对应颜色的PWM通道,其他两个通道设置为0。 4. 当duty_cycle达到max_duty时,方向改为减;当duty_cycle达到0时,方向改为增。 5. 串口中断中,接收三个字节的命令(注意:这里我们假设命令是三个独立的字节,且连续发送,没有其他数据)。当接收到一个字节时,判断是否是命令头,然后接收后续两个字节,组成命令。这里为了简化,我们假设每个命令都是一个字节,并且我们只用一个字节来代表命令(这样更简单,题目要求三个十六进制命令,我们可以定义为三个不同的字节,比如0x01,0x02,0x03)。 但是,题目要求是三个十六进制命令,我们可以这样定义: 命令1:0xAA 命令2:0xBB 命令3:0xCC 串口接收时,每接收到一个字节,判断是否等于这三个命令之一,如果是,则立即切换颜色,并重置呼吸状态(将duty_cycle重置为0,方向设置为增加)。 具体代码实现(基于HAL库,以STM32F103为例): 注意:以下代码是一个简化的示例,实际使用时需要根据硬件连接修改引脚和定时器配置。 步骤: 1. 配置RGB LED的引脚为PWM输出(假设使用PA8(TIM1_CH1,红色)、PA9(TIM1_CH2,绿色)、PA10(TIM1_CH3,蓝色))。 2. 配置定时器TIM1,设置PWM模式。 3. 配置串口(例如USART1),使能接收中断。 4. 配置SysTick定时器,中断时间间隔为10ms(用于更新呼吸灯占空比)。 代码结构: main.c: ```c #include "stm32f1xx_hal.h" // 定义命令 #define CMD_RED 0xAA #define CMD_GREEN 0xBB #define CMD_BLUE 0xCC // 全局变量 TIM_HandleTypeDef htim1; UART_HandleTypeDef huart1; // 呼吸灯相关变量 uint8_t current_color = 0; // 0: red, 1: green, 2: blue uint16_t duty_cycle = 0; // 当前占空比 uint8_t direction = 1; // 1: increase, 0: decrease const uint16_t max_duty = 1000; // 最大占空比,对应定时器的ARR值 const uint16_t step = 10; // 每次增加的步长 void SystemClock_Config(void); static void MX_GPIO_Init(void); static void MX_TIM1_Init(void); static void MX_USART1_UART_Init(void); int main(void) { HAL_Init(); SystemClock_Config(); MX_GPIO_Init(); MX_TIM1_Init(); MX_USART1_UART_Init(); // 启动PWM HAL_TIM_PWM_Start(&htim1, TIM_CHANNEL_1); HAL_TIM_PWM_Start(&htim1, TIM_CHANNEL_2); HAL_TIM_PWM_Start(&htim1, TIM_CHANNEL_3); // 启动串口接收中断 HAL_UART_Receive_IT(&huart1, &rx_data, 1); // 启动SysTick中断(1ms中断一次) HAL_SYSTICK_Config(HAL_RCC_GetHCLKFreq() / 1000); while (1) { // 主循环不处理具体逻辑,都在中断中处理 } } // SysTick中断处理函数(在stm32f1xx_it.c中会调用) void HAL_SYSTICK_Callback(void) { static uint32_t tick_count = 0; tick_count++; // 每10ms更新一次呼吸灯 if (tick_count % 10 == 0) { // 更新占空比 if (direction) { duty_cycle += step; if (duty_cycle >= max_duty) { duty_cycle = max_duty; direction = 0; } } else { duty_cycle -= step; if (duty_cycle <= 0) { duty_cycle = 0; direction = 1; } } // 根据当前颜色设置PWM switch (current_color) { case 0: // red __HAL_TIM_SET_COMPARE(&htim1, TIM_CHANNEL_1, duty_cycle); __HAL_TIM_SET_COMPARE(&htim1, TIM_CHANNEL_2, 0); __HAL_TIM_SET_COMPARE(&htim1, TIM_CHANNEL_3, 0); break; case 1: // green __HAL_TIM_SET_COMPARE(&htim1, TIM_CHANNEL_1, 0); __HAL_TIM_SET_COMPARE(&htim1, TIM_CHANNEL_2, duty_cycle); __HAL_TIM_SET_COMPARE(&htim1, TIM_CHANNEL_3, 0); break; case 2: // blue __HAL_TIM_SET_COMPARE(&htim1, TIM_CHANNEL_1, 0); __HAL_TIM_SET_COMPARE(&htim1, TIM_CHANNEL_2, 0); __HAL_TIM_SET_COMPARE(&htim1, TIM_CHANNEL_3, duty_cycle); break; } } } // 串口接收中断回调函数 uint8_t rx_data; void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart) { if (huart->Instance == USART1) { // 判断接收到的命令 if (rx_data == CMD_RED) { current_color = 0; duty_cycle = 0; // 重置占空比 direction = 1; // 方向设为增加 } else if (rx_data == CMD_GREEN) { current_color = 1; duty_cycle = 0; direction = 1; } else if (rx_data == CMD_BLUE) { current_color = 2; duty_cycle = 0; direction = 1; } // 重新启动接收中断 HAL_UART_Receive_IT(&huart1, &rx_data, 1); } } // 以下为初始化函数,具体配置需要根据硬件调整 void SystemClock_Config(void) { ... } static void MX_GPIO_Init(void) { ... } static void MX_TIM1_Init(void) { // 配置TIM1为PWM模式,ARR设为max_duty(1000),PSC分频得到合适的频率(比如1kHz的PWM频率) // 具体配置代码 } static void MX_USART1_UART_Init(void) { ... } // 在stm32f1xx_it.c中,需要实现SysTick_Handler,它会调用HAL_SYSTICK_IRQHandler,然后调用HAL_SYSTICK_Callback ``` 注意: 1. 上述代码中,我们假设使用TIM1,通道1、2、3分别控制红、绿、蓝。 2. 呼吸灯的更新在SysTick中断中每10ms更新一次(通过计数每10次执行一次)。 3. 串口接收中断中,每接收到一个字节,判断是否是命令,然后切换颜色并重置呼吸状态。 4. 初始化函数(MX_TIM1_Init等)需要根据实际情况配置。 由于具体初始化代码较长,且与硬件相关,这里不展开,但给出TIM1和USART1的初始化示例: TIM1初始化(PWM): ```c static void MX_TIM1_Init(void) { TIM_ClockConfigTypeDef sClockSourceConfig = {0}; TIM_MasterConfigTypeDef sMasterConfig = {0}; TIM_OC_InitTypeDef sConfigOC = {0}; TIM_BreakDeadTimeConfigTypeDef sBreakDeadTimeConfig = {0}; htim1.Instance = TIM1; htim1.Init.Prescaler = 72 - 1; // 72MHz / 72 = 1MHz htim1.Init.CounterMode = TIM_COUNTERMODE_UP; htim1.Init.Period = max_duty - 1; // ARR = max_duty-1,这样PWM频率=1MHz / max_duty = 1kHz htim1.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1; htim1.Init.RepetitionCounter = 0; htim1.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE; if (HAL_TIM_Base_Init(&htim1) != HAL_OK) { Error_Handler(); } sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL; if (HAL_TIM_ConfigClockSource(&htim1, &sClockSourceConfig) != HAL_OK) { Error_Handler(); } if (HAL_TIM_PWM_Init(&htim1) != HAL_OK) { Error_Handler(); } sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET; sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE; if (HAL_TIMEx_MasterConfigSynchronization(&htim1, &sMasterConfig) != HAL_OK) { Error_Handler(); } sConfigOC.OCMode = TIM_OCMODE_PWM1; sConfigOC.Pulse = 0; sConfigOC.OCPolarity = TIM_OCPOLARITY_HIGH; sConfigOC.OCNPolarity = TIM_OCNPOLARITY_HIGH; sConfigOC.OCFastMode = TIM_OCFAST_DISABLE; sConfigOC.OCIdleState = TIM_OCIDLESTATE_RESET; sConfigOC.OCNIdleState = TIM_OCNIDLESTATE_RESET; if (HAL_TIM_PWM_ConfigChannel(&htim1, &sConfigOC, TIM_CHANNEL_1) != HAL_OK) { Error_Handler(); } if (HAL_TIM_PWM_ConfigChannel(&htim1, &sConfigOC, TIM_CHANNEL_2) != HAL_OK) { Error_Handler(); } if (HAL_TIM_PWM_ConfigChannel(&htim1, &sConfigOC, TIM_CHANNEL_3) != HAL_OK) { Error_Handler(); } sBreakDeadTimeConfig.OffStateRunMode = TIM_OSSR_DISABLE; sBreakDeadTimeConfig.OffStateIDLEMode = TIM_OSSI_DISABLE; sBreakDeadTimeConfig.LockLevel = TIM_LOCKLEVEL_OFF; sBreakDeadTimeConfig.DeadTime = 0; sBreakDeadTimeConfig.BreakState = TIM_BREAK_DISABLE; sBreakDeadTimeConfig.BreakPolarity = TIM_BREAKPOLARITY_HIGH; sBreakDeadTimeConfig.AutomaticOutput = TIM_AUTOMATICOUTPUT_DISABLE; if (HAL_TIMEx_ConfigBreakDeadTime(&htim1, &sBreakDeadTimeConfig) != HAL_OK) { Error_Handler(); } HAL_TIM_MspPostInit(&htim1); } ``` USART1初始化: ```c static void MX_USART1_UART_Init(void) { huart1.Instance = USART1; huart1.Init.BaudRate = 9600; huart1.Init.WordLength = UART_WORDLENGTH_8B; huart1.Init.StopBits = UART_STOPBITS_1; huart1.Init.Parity = UART_PARITY_NONE; huart1.Init.Mode = UART_MODE_TX_RX; huart1.Init.HwFlowCtl = UART_HWCONTROL_NONE; huart1.Init.OverSampling = UART_OVERSAMPLING_16; if (HAL_UART_Init(&huart1) != HAL_OK) { Error_Handler(); } } ``` 以上代码仅供参考,实际使用时需要根据开发板的具体情况进行调整。 相关问题: 1. 如果我想实现混合颜色(比如黄色),该如何修改? 2. 如何提高呼吸灯变化的平滑度? 3. 如果串口命令不是单个字节,而是多字节的命令帧(比如0xAA 0x55 0x00),该如何解析? 4. 在切换颜色时,如何实现渐变过渡(淡入淡出)效果?</think>### STM32串口控制彩色呼吸灯实现 以下是使用STM32的串口接收命令控制RGB呼吸灯颜色切换的完整解决方案。系统通过接收三个十六进制命令(0xAA、0xBB、0xCC)分别控制红、绿、蓝三种颜色的呼吸灯效果。 ```c #include "stm32f4xx_hal.h" // 串口定义 UART_HandleTypeDef huart2; // PWM定时器定义 TIM_HandleTypeDef htim3; TIM_OC_InitTypeDef sConfigOC; // 命令定义 #define CMD_RED 0xAA #define CMD_GREEN 0xBB #define CMD_BLUE 0xCC // 呼吸灯控制变量 volatile uint8_t current_color = 0; // 0:红, 1:绿, 2:蓝 volatile uint16_t pwm_duty = 0; // 当前PWM占空比 volatile uint8_t direction = 1; // 呼吸方向: 1=渐强, 0=渐弱 const uint16_t max_duty = 1000; // 最大占空比 int main(void) { HAL_Init(); SystemClock_Config(); MX_GPIO_Init(); MX_USART2_UART_Init(); MX_TIM3_Init(); // 启动PWM通道 HAL_TIM_PWM_Start(&htim3, TIM_CHANNEL_1); // 红色通道 HAL_TIM_PWM_Start(&htim3, TIM_CHANNEL_2); // 绿色通道 HAL_TIM_PWM_Start(&htim3, TIM_CHANNEL_3); // 蓝色通道 // 启动串口接收中断 HAL_UART_Receive_IT(&huart2, &rx_data, 1); // 主循环 while (1) { // 呼吸灯效果更新 update_breathing_effect(); HAL_Delay(10); // 10ms更新周期 } } // 呼吸灯效果更新函数 void update_breathing_effect(void) { // 更新占空比 if (direction) { pwm_duty += 10; if (pwm_duty >= max_duty) direction = 0; } else { pwm_duty -= 10; if (pwm_duty <= 0) direction = 1; } // 根据当前颜色设置PWM switch (current_color) { case 0: // 红色 __HAL_TIM_SET_COMPARE(&htim3, TIM_CHANNEL_1, pwm_duty); __HAL_TIM_SET_COMPARE(&htim3, TIM_CHANNEL_2, 0); __HAL_TIM_SET_COMPARE(&htim3, TIM_CHANNEL_3, 0); break; case 1: // 绿色 __HAL_TIM_SET_COMPARE(&htim3, TIM_CHANNEL_1, 0); __HAL_TIM_SET_COMPARE(&htim3, TIM_CHANNEL_2, pwm_duty); __HAL_TIM_SET_COMPARE(&htim3, TIM_CHANNEL_3, 0); break; case 2: // 蓝色 __HAL_TIM_SET_COMPARE(&htim3, TIM_CHANNEL_1, 0); __HAL_TIM_SET_COMPARE(&htim3, TIM_CHANNEL_2, 0); __HAL_TIM_SET_COMPARE(&htim3, TIM_CHANNEL_3, pwm_duty); break; } } // 串口接收中断回调函数 uint8_t rx_data; void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart) { if (huart == &huart2) { // 处理接收到的命令 switch (rx_data) { case CMD_RED: current_color = 0; // 切换到红色 pwm_duty = 0; // 重置呼吸状态 direction = 1; break; case CMD_GREEN: current_color = 1; // 切换到绿色 pwm_duty = 0; direction = 1; break; case CMD_BLUE: current_color = 2; // 切换到蓝色 pwm_duty = 0; direction = 1; break; } // 重新启用接收中断 HAL_UART_Receive_IT(&huart2, &rx_data, 1); } } // PWM定时器初始化 (TIM3) void MX_TIM3_Init(void) { TIM_ClockConfigTypeDef sClockSourceConfig = {0}; TIM_MasterConfigTypeDef sMasterConfig = {0}; htim3.Instance = TIM3; htim3.Init.Prescaler = 84-1; // 84MHz/84 = 1MHz htim3.Init.CounterMode = TIM_COUNTERMODE_UP; htim3.Init.Period = max_duty-1; // PWM周期 htim3.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1; HAL_TIM_Base_Init(&htim3); sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL; HAL_TIM_ConfigClockSource(&htim3, &sClockSourceConfig); // PWM通道配置 sConfigOC.OCMode = TIM_OCMODE_PWM1; sConfigOC.Pulse = 0; sConfigOC.OCPolarity = TIM_OCPOLARITY_HIGH; sConfigOC.OCFastMode = TIM_OCFAST_DISABLE; HAL_TIM_PWM_ConfigChannel(&htim3, &sConfigOC, TIM_CHANNEL_1); HAL_TIM_PWM_ConfigChannel(&htim3, &sConfigOC, TIM_CHANNEL_2); HAL_TIM_PWM_ConfigChannel(&htim3, &sConfigOC, TIM_CHANNEL_3); sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET; sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE; HAL_TIMEx_MasterConfigSynchronization(&htim3, &sMasterConfig); } // 串口初始化 (USART2) void MX_USART2_UART_Init(void) { huart2.Instance = USART2; huart2.Init.BaudRate = 115200; huart2.Init.WordLength = UART_WORDLENGTH_8B; huart2.Init.StopBits = UART_STOPBITS_1; huart2.Init.Parity = UART_PARITY_NONE; huart2.Init.Mode = UART_MODE_TX_RX; huart2.Init.HwFlowCtl = UART_HWCONTROL_NONE; huart2.Init.OverSampling = UART_OVERSAMPLING_16; HAL_UART_Init(&huart2); } ``` ### 代码解释 1. **命令定义与变量** - 定义了三个十六进制命令:0xAA(红色)、0xBB(绿色)、0xCC(蓝色) - `current_color`:当前显示的颜色(0=红,1=绿,2=蓝) - `pwm_duty`:PWM占空比(0-1000) - `direction`:呼吸方向(1=渐强,0=渐弱) 2. **呼吸灯实现原理** - `update_breathing_effect()`函数每10ms被调用一次 - 通过线性增减PWM占空比实现呼吸效果 - 当占空比达到最大值时反转方向 - 只激活当前颜色的PWM通道 3. **串口命令处理** - 使用中断模式接收串口数据 - 接收到命令后立即切换颜色 - 重置呼吸状态(占空比归零,方向设为渐强) 4. **硬件配置** - 使用TIM3产生PWM信号(通道1-3分别控制RGB LED) - USART2用于串口通信(波特率115200) - PWM频率 = 定时器时钟/(预分频×周期) = 84MHz/(84×1000) = 1kHz ### 硬件连接建议 - **红色LED**:TIM3_CH1 (PA6) - **绿色LED**:TIM3_CH2 (PA7) - **蓝色LED**:TIM3_CH3 (PB0) - **串口**:USART2_TX (PA2), USART2_RX (PA3) ### 使用示例 1. 发送0xAA:切换到红色呼吸灯 2. 发送0xBB:切换到绿色呼吸灯 3. 发送0xCC:切换到蓝色呼吸灯 呼吸效果会自动在选定颜色上循环,新的命令会立即切换颜色并重置呼吸状态。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值