当前位置: 首页 > article >正文

【stm32简单外设篇】- WS2812单线地址式 RGB 灯带

一、适用场景适用场景炫彩灯效跑马、流水、渐变、呼吸、状态指示、可穿戴灯光、舞台/装饰灯带、视觉化数值显示温度/音频以及练习精确时序、DMA定时器应用与实时动画算法。二、器材清单WS2812NeopixelLED 灯带stm32f103或其它 STM32开发板 ×1若干杜邦线母对母/公对母×1组稳定电源3.3V 或 5V按模块标注×1三、工作原理要点WS2812 使用单线串行协议时序严格常见 800kHz每个 LED 接收 24 位数据按 GRB 顺序每个位占用 约 1.25µsTbit。逻辑 0T_H ≈ 0.35µsT_L ≈ 0.9µs逻辑 1T_H ≈ 0.7µsT_L ≈ 0.6µs复位latch脉冲拉低 ≥ 50µsLED 把接收的 24×N 位数据写入并显示。因时序严格常用两种驱动方式软件 bit-bang阻塞屏蔽中断、严格微秒延时逐位输出 —— 实现简单但会阻塞 CPU适合少量 LED / 教学。硬件 PWM DMA用定时器 PWM 输出固定周期例如 1.25µs 分 30 个计数把“0/1”映射为不同的 CCR 值通过 DMA 把整个比特流送入 CCR 寄存器精度高且 CPU 空闲适合中大量灯带我使用的也是这种方法。母头是输入信号公头是输出注意这里的公母头看的是接口的公母不是外壳上的公母四、接线示意白色线→GND红色线→5V/3.3V电源另外还有两根补压线一根5V一根GND当LED灯带超过300个LED灯或灯带长5米时最后进行一个补压操作实际补压时最好大于5V暂时不使用标准库绿色线→PB6HAL库绿色线→PA8五、示例代码标准库#include stm32f10x.h #include stdio.h #include bsp_SysTick.h #include bsp_usart.h #include oled.h #define led_num 10 uint32_t ws2812b_buf[led_num]; uint16_t ws2812b_bit[24*led_num1]; uint16_t showflag0,showtime0; int z; char show[20]; int flag; void WS2812B_IRQHandler(void)//自定义的中断是当DMA搬运完一次数据后才会触发的 { TIM_SetCompare1(TIM4,0);//设置pwm的比较值为0 TIM_Cmd(TIM4,DISABLE);//关闭定时器 flag1; // for(z0;z100000;z); } void WS2812B_ClearBuf(void) { uint8_t i; for(i0;iled_num;i) { ws2812b_buf[i]0x000000;//给每个led写入24位的0即清除颜色 } } void WS2812B_Init(void) { Dma1_SetIRQHandler(WS2812B_IRQHandler);//指定DMA中断后执行的中断函数并不是用系统的函数 Dma1_Configuare((uint32_t)(ws2812b_bit));//指定DMA帮运的数组 Remote_Init();//进行TIM的初始化 } void WS2812B_SetBuf(uint32_t color) { uint8_t i; for(i0;iled_num;i) { ws2812b_buf[i]color;//循环给每个led赋值24位的颜色数据 } } void WS2812B_UpdateBuf(void)//将所有灯的颜色数据进行全部更新 { uint8_t i,j; for(j0;jled_num;j)//遍历每个灯 { for(i0;i24;i)//遍历每个灯的颜色位 { if(ws2812b_buf[j](0x800000i))//判断每个灯的24位颜色数据如果为0则定义其对应的PWM数据为30反之为60 ws2812b_bit[j*24i]60; else ws2812b_bit[j*24i]30; } Dma1_Configuare((uint32_t)(ws2812b_bit));//将更新好的数组重新赋值到DMA初始化中 Dma1_start(24*led_num);//启动DMA并将一次要搬运的数据个数指明 Time4_run(ENABLE);//开启定时器 while(flag0);//等待数据搬运完成 flag0; } } void init_ALL(void) { WS2812B_Init(); WS2812B_ClearBuf(); tim2_Init(); } void showbreath() { static uint8_t i,color; showtime6;//设置运行时间 if(i0)WS2812B_SetBuf((color)); if(i1)WS2812B_SetBuf((255-color)); if(i2)WS2812B_SetBuf((color)8); if(i3)WS2812B_SetBuf((255-color)8); if(i4)WS2812B_SetBuf((color)16); if(i5)WS2812B_SetBuf((255-color)16); if(i6)WS2812B_SetBuf((color)|(color)8); if(i7)WS2812B_SetBuf((255-color)|(255-color)8); if(i8)WS2812B_SetBuf((color)|(color)16); if(i9)WS2812B_SetBuf((255-color)|(255-color)16); if(i10)WS2812B_SetBuf(((color)8)|((color)16)); if(i11)WS2812B_SetBuf(((255-color)8)|((255-color)16)); if(i12)WS2812B_SetBuf(((color))|((color)8)|((color)16)); if(i13)WS2812B_SetBuf(((255-color))|((255-color)8)|((255-color)16)); color; if(color0) { i; i%14; } } void startshow() { uint8_t i,num; uint32_t R,G,B; static uint8_t j; showtime20; for(iled_num;i0;i--) { ws2812b_buf[i]ws2812b_buf[i-1]; } if(j0) { numrand()%7; if(num0)ws2812b_buf[0]0x0000ff; if(num1)ws2812b_buf[0]0x00ff00; if(num2)ws2812b_buf[0]0xff0000; if(num3)ws2812b_buf[0]0x00ffff; if(num4)ws2812b_buf[0]0xff00ff; if(num5)ws2812b_buf[0]0xffff00; if(num6)ws2812b_buf[0]0xffffff; }else if(j15) { Rws2812b_buf[1]/0X100%0x100; Gws2812b_buf[1]/0X10000%0x100; Bws2812b_buf[1]%0x100; if(G20) G-20; if(R20) R-20; if(B20) B-20; ws2812b_buf[0](G16)|(R8)|B; }else{ ws2812b_buf[0]0; } j; j%50; } int main(void) { init_ALL(); USART_Config(); while(1) { if(showflag 1)//定时一段时间后会自动开启颜色设置 { showflag0; startshow();//将颜色数据写入数组中 // showbreath();//将颜色数据写入数组中 WS2812B_UpdateBuf();//将数组数据更新到DMA中有DMA自行去将数据写入到LED灯带中 } } } #include bsp_SysTick.h extern uint16_t showflag,showtime; void Remote_Init(void) { GPIO_InitTypeDef GPIO_InitStructure; TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure; TIM_OCInitTypeDef TIM_OCInitStruct; RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB|RCC_APB2Periph_AFIO,ENABLE); //使能PORTB时钟 RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4,ENABLE); //TIM4 时钟使能 GPIO_InitStructure.GPIO_Pin GPIO_Pin_6; //PB9 输入 GPIO_InitStructure.GPIO_Mode GPIO_Mode_AF_PP; //上拉输入 GPIO_InitStructure.GPIO_Speed GPIO_Speed_50MHz; GPIO_Init(GPIOB, GPIO_InitStructure); // GPIO_SetBits(GPIOB,GPIO_Pin_9); //初始化GPIOB.9 TIM_TimeBaseStructure.TIM_Period 90-1; //设定计数器自动重装值 最大10ms溢出 TIM_TimeBaseStructure.TIM_Prescaler 1-1; //配置输入分频,不分频,即72MHz约等于1/72MHz0.00000001s10ns TIM_TimeBaseStructure.TIM_ClockDivision TIM_CKD_DIV1; //设置时钟分割:TDTS Tck_tim TIM_TimeBaseStructure.TIM_CounterMode TIM_CounterMode_Up; //TIM向上计数模式 TIM_TimeBaseInit(TIM4, TIM_TimeBaseStructure); //根据指定的参数初始化TIMx TIM_OCInitStruct.TIM_OCMode TIM_OCMode_PWM1; // 选择输入端 IC4映射到TI4上 TIM_OCInitStruct.TIM_OCPolarity TIM_OCPolarity_High; //上升沿捕获 TIM_OCInitStruct.TIM_OutputState TIM_OutputState_Enable; TIM_OCInitStruct.TIM_Pulse 0; //配置输入分频,不分频 TIM_OC1Init(TIM4, TIM_OCInitStruct);//初始化定时器输入捕获通道 TIM_DMAConfig(TIM4,TIM_DMABase_CCR1,TIM_DMABurstLength_1Transfer); TIM_DMACmd(TIM4,TIM_DMA_Update,ENABLE); TIM_Cmd(TIM4,DISABLE); } void tim2_Init(void) { NVIC_InitTypeDef NVIC_InitStructure; TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure; RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE); //TIM2 时钟使能 TIM_TimeBaseStructure.TIM_Period 1000; //设定计数器自动重装值 最大1ms溢出 TIM_TimeBaseStructure.TIM_Prescaler (72-1); //预分频器,1M的计数频率,1us加1. TIM_TimeBaseStructure.TIM_ClockDivision TIM_CKD_DIV1; //设置时钟分割:TDTS Tck_tim TIM_TimeBaseStructure.TIM_CounterMode TIM_CounterMode_Up; //TIM向上计数模式 TIM_TimeBaseStructure.TIM_RepetitionCounter 0 ; TIM_TimeBaseInit(TIM2, TIM_TimeBaseStructure); //根据指定的参数初始化TIMx TIM_ClearFlag(TIM2,TIM_FLAG_Update);//手动把更新中断标志位清除一下解决刚初始化完就进入中断计数从1开始的问题 TIM_ITConfig(TIM2,TIM_IT_Update,ENABLE); NVIC_InitStructure.NVIC_IRQChannel TIM2_IRQn; //TIM2中断 NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority 1; NVIC_InitStructure.NVIC_IRQChannelSubPriority 3; //从优先级3级 NVIC_InitStructure.NVIC_IRQChannelCmd ENABLE; //IRQ通道被使能 NVIC_Init(NVIC_InitStructure); //根据NVIC_InitStruct中指定的参数初始化外设NVIC寄存器 TIM_Cmd(TIM2,ENABLE ); //使能定时器2 } void Time4_run(FunctionalState NewState)//控制TIM的运行 { TIM_Cmd(TIM4,NewState); } void Time4_SetCompare(uint16_t Compare1)//控制TIM的比较值 { TIM_SetCompare1(TIM4,Compare1); } void TIM2_IRQHandler(void) { static uint16_t rupttime; if(TIM_GetITStatus(TIM2,TIM_IT_Update)!RESET)//计时器更新中断 { rupttime; if(rupttime showtime)//超时打开标志位去设置led的数据并打开灯 { showflag1; rupttime0; } } TIM_ClearITPendingBit(TIM2,TIM_IT_Update); }HAL库/* USER CODE BEGIN Header */ /** ****************************************************************************** * file : main.c * brief : Main program body ****************************************************************************** * attention * * Copyright (c) 2025 STMicroelectronics. * All rights reserved. * * This software is licensed under terms that can be found in the LICENSE file * in the root directory of this software component. * If no LICENSE file comes with this software, it is provided AS-IS. * ****************************************************************************** */ /* USER CODE END Header */ /* Includes ------------------------------------------------------------------*/ #include main.h /* Private includes ----------------------------------------------------------*/ /* USER CODE BEGIN Includes */ #include more-led.h /* USER CODE END Includes */ /* Private typedef -----------------------------------------------------------*/ /* USER CODE BEGIN PTD */ /* USER CODE END PTD */ /* Private define ------------------------------------------------------------*/ /* USER CODE BEGIN PD */ /* USER CODE END PD */ /* Private macro -------------------------------------------------------------*/ /* USER CODE BEGIN PM */ /* USER CODE END PM */ /* Private variables ---------------------------------------------------------*/ TIM_HandleTypeDef htim1; TIM_HandleTypeDef htim2; DMA_HandleTypeDef hdma_tim1_ch1; UART_HandleTypeDef huart2; /* USER CODE BEGIN PV */ /* USER CODE END PV */ /* Private function prototypes -----------------------------------------------*/ void SystemClock_Config(void); static void MX_GPIO_Init(void); static void MX_DMA_Init(void); static void MX_TIM1_Init(void); static void MX_TIM2_Init(void); static void MX_USART2_UART_Init(void); /* USER CODE BEGIN PFP */ /* USER CODE END PFP */ /* Private user code ---------------------------------------------------------*/ /* USER CODE BEGIN 0 */ extern uint16_t showflag; /* USER CODE END 0 */ /** * brief The application entry point. * retval int */ int main(void) { /* USER CODE BEGIN 1 */ /* USER CODE END 1 */ /* MCU Configuration--------------------------------------------------------*/ /* Reset of all peripherals, Initializes the Flash interface and the Systick. */ HAL_Init(); /* USER CODE BEGIN Init */ /* USER CODE END Init */ /* Configure the system clock */ SystemClock_Config(); /* USER CODE BEGIN SysInit */ /* USER CODE END SysInit */ /* Initialize all configured peripherals */ MX_GPIO_Init(); MX_DMA_Init(); MX_TIM1_Init(); MX_TIM2_Init(); MX_USART2_UART_Init(); /* USER CODE BEGIN 2 */ init_ALL(); HAL_TIM_Base_Start_IT(htim2); /* USER CODE END 2 */ /* Infinite loop */ /* USER CODE BEGIN WHILE */ while (1) { if(showflag 1)//定时一段时间后会自动开启颜色设置 { showflag0; // startshow();//将颜色数据写入数组中 showbreath();//将颜色数据写入数组中 WS2812B_UpdateBuf();//将数组数据更新到DMA中有DMA自行去将数据写入到LED灯带中 } /* USER CODE END WHILE */ /* USER CODE BEGIN 3 */ } /* USER CODE END 3 */ } /** * brief System Clock Configuration * retval None */ void SystemClock_Config(void) { RCC_OscInitTypeDef RCC_OscInitStruct {0}; RCC_ClkInitTypeDef RCC_ClkInitStruct {0}; /** Initializes the RCC Oscillators according to the specified parameters * in the RCC_OscInitTypeDef structure. */ RCC_OscInitStruct.OscillatorType RCC_OSCILLATORTYPE_HSE; RCC_OscInitStruct.HSEState RCC_HSE_ON; RCC_OscInitStruct.HSEPredivValue RCC_HSE_PREDIV_DIV1; RCC_OscInitStruct.HSIState RCC_HSI_ON; RCC_OscInitStruct.PLL.PLLState RCC_PLL_ON; RCC_OscInitStruct.PLL.PLLSource RCC_PLLSOURCE_HSE; RCC_OscInitStruct.PLL.PLLMUL RCC_PLL_MUL9; if (HAL_RCC_OscConfig(RCC_OscInitStruct) ! HAL_OK) { Error_Handler(); } /** Initializes the CPU, AHB and APB buses clocks */ RCC_ClkInitStruct.ClockType RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK |RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2; RCC_ClkInitStruct.SYSCLKSource RCC_SYSCLKSOURCE_PLLCLK; RCC_ClkInitStruct.AHBCLKDivider RCC_SYSCLK_DIV1; RCC_ClkInitStruct.APB1CLKDivider RCC_HCLK_DIV2; RCC_ClkInitStruct.APB2CLKDivider RCC_HCLK_DIV1; if (HAL_RCC_ClockConfig(RCC_ClkInitStruct, FLASH_LATENCY_2) ! HAL_OK) { Error_Handler(); } } /** * brief TIM1 Initialization Function * param None * retval None */ static void MX_TIM1_Init(void) { /* USER CODE BEGIN TIM1_Init 0 */ /* USER CODE END TIM1_Init 0 */ TIM_MasterConfigTypeDef sMasterConfig {0}; TIM_OC_InitTypeDef sConfigOC {0}; TIM_BreakDeadTimeConfigTypeDef sBreakDeadTimeConfig {0}; /* USER CODE BEGIN TIM1_Init 1 */ /* USER CODE END TIM1_Init 1 */ htim1.Instance TIM1; htim1.Init.Prescaler 0; htim1.Init.CounterMode TIM_COUNTERMODE_UP; htim1.Init.Period 89; htim1.Init.ClockDivision TIM_CLOCKDIVISION_DIV1; htim1.Init.RepetitionCounter 0; htim1.Init.AutoReloadPreload TIM_AUTORELOAD_PRELOAD_DISABLE; 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(); } 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(); } /* USER CODE BEGIN TIM1_Init 2 */ /* USER CODE END TIM1_Init 2 */ HAL_TIM_MspPostInit(htim1); } /** * brief TIM2 Initialization Function * param None * retval None */ static void MX_TIM2_Init(void) { /* USER CODE BEGIN TIM2_Init 0 */ /* USER CODE END TIM2_Init 0 */ TIM_ClockConfigTypeDef sClockSourceConfig {0}; TIM_MasterConfigTypeDef sMasterConfig {0}; /* USER CODE BEGIN TIM2_Init 1 */ /* USER CODE END TIM2_Init 1 */ htim2.Instance TIM2; htim2.Init.Prescaler 71; htim2.Init.CounterMode TIM_COUNTERMODE_UP; htim2.Init.Period 999; htim2.Init.ClockDivision TIM_CLOCKDIVISION_DIV1; htim2.Init.AutoReloadPreload TIM_AUTORELOAD_PRELOAD_DISABLE; if (HAL_TIM_Base_Init(htim2) ! HAL_OK) { Error_Handler(); } sClockSourceConfig.ClockSource TIM_CLOCKSOURCE_INTERNAL; if (HAL_TIM_ConfigClockSource(htim2, sClockSourceConfig) ! HAL_OK) { Error_Handler(); } sMasterConfig.MasterOutputTrigger TIM_TRGO_RESET; sMasterConfig.MasterSlaveMode TIM_MASTERSLAVEMODE_DISABLE; if (HAL_TIMEx_MasterConfigSynchronization(htim2, sMasterConfig) ! HAL_OK) { Error_Handler(); } /* USER CODE BEGIN TIM2_Init 2 */ /* USER CODE END TIM2_Init 2 */ } /** * brief USART2 Initialization Function * param None * retval None */ static void MX_USART2_UART_Init(void) { /* USER CODE BEGIN USART2_Init 0 */ /* USER CODE END USART2_Init 0 */ /* USER CODE BEGIN USART2_Init 1 */ /* USER CODE END USART2_Init 1 */ 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; if (HAL_UART_Init(huart2) ! HAL_OK) { Error_Handler(); } /* USER CODE BEGIN USART2_Init 2 */ /* USER CODE END USART2_Init 2 */ } /** * Enable DMA controller clock */ static void MX_DMA_Init(void) { /* DMA controller clock enable */ __HAL_RCC_DMA1_CLK_ENABLE(); /* DMA interrupt init */ /* DMA1_Channel2_IRQn interrupt configuration */ HAL_NVIC_SetPriority(DMA1_Channel2_IRQn, 0, 0); HAL_NVIC_EnableIRQ(DMA1_Channel2_IRQn); } /** * brief GPIO Initialization Function * param None * retval None */ static void MX_GPIO_Init(void) { /* USER CODE BEGIN MX_GPIO_Init_1 */ /* USER CODE END MX_GPIO_Init_1 */ /* GPIO Ports Clock Enable */ __HAL_RCC_GPIOD_CLK_ENABLE(); __HAL_RCC_GPIOA_CLK_ENABLE(); /* USER CODE BEGIN MX_GPIO_Init_2 */ /* USER CODE END MX_GPIO_Init_2 */ } /* USER CODE BEGIN 4 */ /* USER CODE END 4 */ /** * brief This function is executed in case of error occurrence. * retval None */ void Error_Handler(void) { /* USER CODE BEGIN Error_Handler_Debug */ /* User can add his own implementation to report the HAL error return state */ __disable_irq(); while (1) { } /* USER CODE END Error_Handler_Debug */ } #ifdef USE_FULL_ASSERT /** * brief Reports the name of the source file and the source line number * where the assert_param error has occurred. * param file: pointer to the source file name * param line: assert_param error line source number * retval None */ void assert_failed(uint8_t *file, uint32_t line) { /* USER CODE BEGIN 6 */ /* User can add his own implementation to report the file name and line number, ex: printf(Wrong parameters value: file %s on line %d\r\n, file, line) */ /* USER CODE END 6 */ } #endif /* USE_FULL_ASSERT */ #include more-led.h DMA_HandleTypeDef hdma_tim4_ch1; extern TIM_HandleTypeDef htim1; extern TIM_HandleTypeDef htim2; extern DMA_HandleTypeDef hdma_tim4_up; volatile uint32_t ADC_ConvertedValue[2]{0}; /* 颜色位缓冲区和实际 LED 数据缓冲 */ uint32_t ws2812b_buf[LED_NUM]; uint16_t ws2812b_bit[LED_NUM * 24]; /* 传输完成标志与回调指针 */ volatile uint8_t flag 0; uint16_t showtime; uint8_t showflag; /* 存放用户回调 */ static DMA_CallbackTypeDef user_dma_cb NULL; //static void WS2812B_IRQHandler(void) //{ // /* 关闭 PWM 输出——恢复为零电平 */ // __HAL_TIM_SET_COMPARE(htim4, TIM_CHANNEL_1, 0); // /* 停止 TIM4 PWM */ // HAL_TIM_PWM_Stop(htim4, TIM_CHANNEL_1); // // /* 通知上层数据已全部发送完 */ // flag 1; //} /*------------------------------------------------------------------------------*/ /* 2. 清空 LED 数据缓冲所有颜色置黑 */ void WS2812B_ClearBuf(void) { for (uint8_t i 0; i LED_NUM; i) { ws2812b_buf[i] 0x000000; } } /*------------------------------------------------------------------------------*/ /* 4. 设置所有 LED 同一个颜色 */ void WS2812B_SetBuf(uint32_t color) { for (uint8_t i 0; i LED_NUM; i) { ws2812b_buf[i] color; } } /*------------------------------------------------------------------------------*/ /* 5. 更新并发送整个 LED 缓冲区 */ void WS2812B_UpdateBuf(void) { for (uint8_t j 0; j LED_NUM; j) { uint32_t c ws2812b_buf[j]; /* 逐 LED 展开为 24bit PWM 数据流 */ for (uint8_t i 0; i 24; i) { ws2812b_bit[j*24 i] (c (1UL (23 - i))) ? 60 : 30; // if (ws2812b_buf[j] (0x800000 i)) // ws2812b_bit[j * 24 i] 60; /* “1” 对应高电平时长 */ // else // ws2812b_bit[j * 24 i] 30; /* “0” 对应高电平时长 */ } } Time4_run(ENABLE); // 启动 TIM4 DMA while (flag 0) { } flag 0; } /*------------------------------------------------------------------------------*/ /* 6. 系统初始化入口清缓冲并启动周期中断TIM2 */ void init_ALL(void) { WS2812B_ClearBuf(); /* TIM2 中断由 CubeMX 的 HAL_TIM_Base_Start_IT(htim2) 在 main() 中启动 */ } /*------------------------------------------------------------------------------*/ /* 7. 呼吸灯效果驱动每次调用更新一次显示缓冲和定时参数 */ void showbreath(void) { static uint8_t idx 0, color 0; showtime 4; /* 由 TIM2 中断控制刷新周期 */ switch (idx) { case 0: WS2812B_SetBuf( color); break; case 1: WS2812B_SetBuf((255 - color)); break; case 2: WS2812B_SetBuf( color 8); break; case 3: WS2812B_SetBuf((255 - color) 8); break; case 4: WS2812B_SetBuf( color 16); break; case 5: WS2812B_SetBuf((255 - color) 16); break; case 6: WS2812B_SetBuf(( color) | ( color 8)); break; case 7: WS2812B_SetBuf(((255 - color)) | ((255 - color) 8)); break; case 8: WS2812B_SetBuf(( color) | ( color 16)); break; case 9: WS2812B_SetBuf(((255 - color)) | ((255 - color) 16)); break; case 10: WS2812B_SetBuf(( color 8) | ( color 16)); break; case 11: WS2812B_SetBuf(((255 - color) 8) | ((255 - color) 16)); break; case 12: WS2812B_SetBuf(( color) | ( color 8) | ( color 16)); break; case 13: WS2812B_SetBuf(((255 - color)) | ((255 - color) 8) | ((255 - color) 16)); break; } color; if (color 0) { idx (idx 1) % 14; } } /*------------------------------------------------------------------------------*/ /* 8. 滚动色带/随机色效果 */ void startshow(void) { static uint8_t j 0; uint8_t num; uint32_t R, G, B; showtime 20; /* 数据右移一位为最新色腾出位置 */ for (uint8_t i LED_NUM - 1; i 0; i--) { ws2812b_buf[i] ws2812b_buf[i - 1]; } if (j 0) { num rand() % 7; const uint32_t colors[7] { 0x0000FF, 0x00FF00, 0xFF0000, 0x00FFFF, 0xFF00FF, 0xFFFF00, 0xFFFFFF }; ws2812b_buf[0] colors[num]; } else if (j 15) { Rws2812b_buf[1]/0X100%0x100; Gws2812b_buf[1]/0X10000%0x100; Bws2812b_buf[1]%0x100; if (G 20) G - 20; if (R 20) R - 20; if (B 20) B - 20; ws2812b_buf[0] (G 16) | (R 8) | B; } else { ws2812b_buf[0] 0; } j (j 1) % 50; } void HAL_TIM_PWM_PulseFinishedCallback(TIM_HandleTypeDef *htim) { __HAL_TIM_SET_COMPARE(htim1, TIM_CHANNEL_1, 0); HAL_TIM_PWM_Stop_DMA(htim1,TIM_CHANNEL_1); flag 1; } /** * brief 启停 TIM4 PWM / DMA * param NewState: ENABLE 或 DISABLE */ void Time4_run(FunctionalState NewState) { if (NewState) { HAL_TIM_PWM_Start_DMA(htim1,TIM_CHANNEL_1,(uint32_t *)ws2812b_bit,LED_NUM * 24); } else { HAL_TIM_PWM_Stop_DMA(htim1, TIM_CHANNEL_1); } } /** * brief 更新 TIM4 的比较寄存器 CCR1 值用于输出PWM控制LED * param Compare1: 新的比较值 */ void Time4_SetCompare(uint16_t Compare1) { __HAL_TIM_SET_COMPARE(htim1, TIM_CHANNEL_1, Compare1); } /** * brief TIM 中断定时到达回调弱定义可被重载 * param htim: 产生中断的定时器句柄 */ void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim) { static uint16_t rupttime 0; if (htim-Instance TIM2) { rupttime; if (rupttime showtime) { showflag 1; rupttime 0; } } } #ifndef _ADC_TIME_H #define _ADC_TIME_H #include main.h #define LED_NUM 10 /* 用户用来接收 DMA 完成回调的函数指针类型 */ typedef void (*DMA_CallbackTypeDef)(void); static void WS2812B_IRQHandler(void); void WS2812B_ClearBuf(void); void WS2812B_Init(void); void WS2812B_SetBuf(uint32_t color); void WS2812B_UpdateBuf(void); void init_ALL(void); void showbreath(void); void startshow(void); void Dma1_SetIRQHandler(DMA_CallbackTypeDef cb); void Dma1_start(uint16_t DataNumber); void Time4_run(FunctionalState NewState); void Time4_SetCompare(uint16_t Compare1); void Dma1_Configuare(uint32_t MemoryBaseAddr); #endif六、讲解视频https://www.bilibili.com/video/BV1hvNNzaE3d/?spm_id_from333.1387.homepage.video_card.clickvd_sourceb035825fef3be39bc47a9c50b324d086https://www.bilibili.com/video/BV1wiNKzQE8v/?spm_id_from333.1387.homepage.video_card.clickvd_sourceb035825fef3be39bc47a9c50b324d086https://www.bilibili.com/video/BV1ZtNuzXEod/?spm_id_from333.1387.homepage.video_card.clickvd_sourceb035825fef3be39bc47a9c50b324d086​

相关文章:

【stm32简单外设篇】- WS2812单线地址式 RGB 灯带

一、适用场景 适用场景:炫彩灯效(跑马、流水、渐变、呼吸)、状态指示、可穿戴灯光、舞台/装饰灯带、视觉化数值显示(温度/音频)以及练习精确时序、DMA定时器应用与实时动画算法。 二、器材清单 WS2812(Ne…...

基于SpringBoot+Vue的Spring Boot阳光音乐厅订票系统管理系统设计与实现【Java+MySQL+MyBatis完整源码】

💡实话实说:C有自己的项目库存,不需要找别人拿货再加价。摘要 随着互联网技术的快速发展和人们生活水平的不断提高,线上娱乐消费需求日益增长,音乐演出市场呈现出蓬勃发展的态势。传统的线下购票方式存在排队时间长、信…...

Ubuntu 上安装 ping 和 nslookup 命令

Ubuntu 上安装 ping 和 nslookup 命令 在使用 Ubuntu 系统时,ping 和 nslookup 是我们最常用的网络诊断工具。然而,有时当你尝试运行它们时,系统却提示“command not found”。这通常发生在 Ubuntu Server 最小化安装或 Docker 容器环境中。本…...

Ubuntu 系统 libc6-dev 依赖冲突解决

Ubuntu 系统 libc6-dev 依赖冲突:原因分析与两种修复方案 问题现象 在 Ubuntu 系统中执行以下命令安装 C 语言开发库时: sudo apt install libc6-dev出现如下依赖错误: The following packages have unmet dependencies:libc6-dev : Depends:…...

圆锥破碎机图

圆锥破碎机作为矿山、建材等领域的核心设备,其核心作用在于通过层压破碎原理实现物料的高效破碎。设备运行时,动锥在偏心轴套的驱动下绕固定轴做旋摆运动,与定锥形成不断变化的破碎腔,物料在腔体内受到挤压、弯曲和剪切力的综合作…...

小带轮(同步带)——CAD

小带轮作为同步带传动系统的核心组件,其设计精度直接影响动力传递的效率与稳定性。在机械传动领域,同步带传动凭借无滑移、传动比精准的特性,广泛应用于数控机床、自动化设备及精密仪器中。小带轮通过与同步带齿槽的精确啮合,将旋…...

前后端分离影院购票系统系统|SpringBoot+Vue+MyBatis+MySQL完整源码+部署教程

💡实话实说:有自己的项目库存,不需要找别人拿货再加价,所以能给到超低价格。摘要 随着互联网技术的快速发展,传统影院购票方式已无法满足用户对便捷性和实时性的需求。影院购票系统的数字化和智能化转型成为行业趋势&a…...

Java Web Spring Boot律师事务所案件管理系统系统源码-SpringBoot2+Vue3+MyBatis-Plus+MySQL8.0【含文档】

💡实话实说: 有自己的项目库存,不需要找别人拿货再加价,所以能给到超低价格。 摘要 随着信息技术的快速发展,传统律师事务所的案件管理方式逐渐暴露出效率低下、数据冗余和安全性不足等问题。律师事务所案件管理系统的…...

前后端分离Spring Boot大学校园生活信息平台系统|SpringBoot+Vue+MyBatis+MySQL完整源码+部署教程

💡实话实说:有自己的项目库存,不需要找别人拿货再加价,所以能给到超低价格。摘要 随着信息技术的快速发展,大学校园管理逐渐向数字化、智能化方向转型。传统的校园信息管理方式存在信息孤岛、效率低下、交互体验差等问…...

K8S HPA自动扩缩容实战指南

文章目录前言1、简介2、工作原理3、HPA版本变革4、监控指标类型4、HPA配置资源清单4.1 基于CPU负载实现自动扩缩容NAME:这是 HPA 对象的名称REFERENCE:HPA 监控和自动扩展的目标对象。TARGETS:当前和目标的资源使用情况MINPODS:HP…...

Windows 上彻底卸载 Node.js

一、前言 电脑环境混乱,记录一下完整卸载的过程。 二、卸载流程 2.1 控制面板卸载 打开控制面报点击程序 -> 卸载程序找到 nodejs选择更加提示完成卸载 2.2 删除残留的文件 这里我按照默认按照路径操作,如果你自己安装的时候自定义了,…...

Redis分片集群散列插槽

一、前言:为什么 Redis Cluster 用 16384 个槽?在 Redis 分片集群(Cluster)中,数据不是随机分布,而是通过“散列插槽”(Hash Slot)机制进行分片。 你可能好奇:为什么是 1…...

大模型和芯片工程师都会犯错——凭什么用零缺陷标准要求前者?

跨时钟域漏处理、验证约束文件过约束了——这些问题在流片前几乎每个项目都会遇到。没有人会因为工程师犯了这些错误就说"这个人不能用"。那为什么大模型一旦出错,就有人开始质疑它的价值?这个双重标准值得认真想一想。现在很多团队在用大模型…...

投研人如何养“虾“?

扫描下载文档详情页: https://www.didaidea.com/wenku/16586.html...

NumPy:ndarray 数组属性

在使用 NumPy 进行科学计算或数据分析时,数组对象不仅存储数据本身,还包含描述数组结构与内存布局的信息,这些信息被称为数组属性(array attributes)。通过这些属性,我们可以了解数组的维度结构、元素数量、…...

WinClaw CLI 工具开发指南

你可以给 AI 一个工具。但更好的做法是:告诉它怎么发现工具、怎么理解工具、怎么组合工具。 WinClaw 的 CLI 工具体系,就是为此而设计的。一、AI 时代,工具开发的逻辑变了过去做 CLI 工具,用户是人。帮助文档写给人看,…...

Debian/Ubuntu 18.04 上安装 GLIBC 2.28 (2026)

Ubuntu 18.04 上安装 GLIBC 2.28 (2026) 引言 某些现代软件(例如 Visual Studio Code Server 1.88 及更新版本)要求系统 GLIBC 版本不低于 2.28,而 Ubuntu 18.04 默认提供的是 GLIBC 2.27。直接升级系统级 GLIBC 可能导…...

在Linux x86_64系统中编译mission

以下在ubuntu18.10 gcc8.3编译通过。安装编译工具:sudo apt install build-essential sudo apt install cmake重命名以下目录中的wsf_module为wsf_module-(子目录的不用管)以屏蔽图形界面相关的模块:swdev/src/engage swdev/src/…...

黑客与画家的品牌时代主动式Agent时代的品牌#The Brand Age

主动式 Agent 时代的品牌Paul Graham 在《品牌时代》(The Brand Age)中复盘了瑞士钟表业如何从“精准计时”的黄金时代,转型为“身份象征”的品牌时代。他提出了一个冷峻的定义:当产品之间的实质性差异消失时,品牌就是…...

告别OSPF!EVE-NG专业版+BGP Unnumbered打通Underlay的完整实战

吃一堑,长一智。有了前面的经验教训(ECN配置折戟记:vEOS模拟器局限性深度剖析),我们这次在换镜像的时候,提前把后面实验用到的命令先验证一下(从屡战屡败到一气呵成:EVE-NG专业版 C…...

威拉里发布多款金属3D打印新材料!三期项目与国外工厂全力推进!

当前,3D打印正迈入规模化生产新阶段,金属粉末的品质一致性与供应稳定性,直接决定了规模化生产的可行性与经济性。近日,国内3D打印金属粉末领域的龙头企业威拉里,接连发布多款针对不同高端制造领域的新型金属粉末材料&a…...

深入理解 Dify 插件守护进程:从加载到执行的完整链路

❝本文深入剖析 Dify 插件系统的核心机制,揭秘插件守护进程如何加载、启动和执行插件代码,以及参数传递的完整链路。❞一、前言Dify 作为一款开源的 LLM 应用开发平台,其插件系统是扩展平台能力的核心机制。很多开发者在阅读源码时会产生疑问…...

探索大数据领域Flink的CEP复杂事件处理

探索大数据领域Flink的CEP复杂事件处理 Keywords: Apache Flink, Complex Event Processing (CEP), 大数据实时分析, 事件流, 模式检测, 状态机, 实时报警系统 Summary: 本文将带您深入探索Apache Flink中的复杂事件处理(CEP)技术,一种在大数…...

当SSD退役时必做的5件事:基于NVMe Sanitize的完整数据销毁流程

NVMe SSD退役数据销毁全指南:从Sanitize操作到二手处置 当企业级NVMe SSD面临退役时,数据安全销毁是技术团队必须严肃对待的环节。一块未经妥善处理的存储设备,即使被标记为"已删除",仍可能通过专业工具恢复敏感数据。本…...

4步突破:Cursor无限制使用完全指南

4步突破:Cursor无限制使用完全指南 【免费下载链接】go-cursor-help 解决Cursor在免费订阅期间出现以下提示的问题: Youve reached your trial request limit. / Too many free trial accounts used on this machine. Please upgrade to pro. We have this limit in…...

问题解决方法:keil软件用st-link烧录代码报错

问题:今天用st-link烧录代码报错说芯片和识别的芯片不一样?难道是盗版?解决方法:把这个使能引脚取消就可以成功烧录了...

SQL Server查看数据库中每张表的数据量和总数据量

查询将返回每个表的名称和表中的数据行数(RowCounts),并按数据量从大到小排序 -- 查询数据库中每张表的行数(数据量) SELECT t.name AS TableName, -- 表的名称SUM(p.rows) AS RowCounts -- 表中所有分区的行数之和…...

GME-Qwen2-VL-2B辅助AE视频制作:智能生成视频片段描述与标签

GME-Qwen2-VL-2B辅助AE视频制作:智能生成视频片段描述与标签 1. 引言 如果你经常用After Effects做视频,肯定遇到过这样的场景:项目文件夹里塞满了各种素材片段,时间线拉得老长,想找一个特定镜头或者回忆某个片段的用…...

MATLAB麦克风实时采集与波形显示:两种方法对比与性能优化

MATLAB麦克风实时采集与波形显示:两种方法对比与性能优化 在音频信号处理领域,实时采集与可视化是许多应用的基础环节。无论是语音识别系统开发、环境噪声监测,还是音乐分析工具构建,快速准确地获取声音波形并实时显示都是关键的第…...

Phi-3-mini-4k-instruct Ollama镜像免配置教程:零基础快速上手文本生成

Phi-3-mini-4k-instruct Ollama镜像免配置教程:零基础快速上手文本生成 你是不是也想体验最新的人工智能文本生成,但被复杂的安装配置劝退了?今天我要介绍的Phi-3-mini-4k-instruct镜像,让你完全跳过所有技术门槛,直接…...