下面的分析仅从我个人角度出发,比较菜,勿喷
能量机关分为两部分:一部分为流水灯,一部分为常亮;可都采用常亮模式,运用到的接口会少些,但是为了方便视觉识别,所以两部分都要实现
网络上有关于控制灯条的多种方法,使用GPIO、PWM+DMA、SPI+DMA等各种方法,还是选择了PWM+DMA的方式去控制,因为开源资料太多了。
优点:DMA转运,硬件自动数据搬运(由内存到外设),减少CPU资源占用
一块扇叶由两部分组成,一部分为绿色区域,一部分为红色区域,需要两个PWM通道同时控制一片扇叶,以达到相同时间实现两种不同效果;所以所需PWM通道=5*2=10
对比之下,C板有8个PWM,A板有16个PWM,虽然STM32F103的板子开源很多,但是用的太多太麻烦了,不如一块板子解决问题。
可编程彩灯,型号不一样,所以对应的时序波形图也不一样,这里仅作简单的说明:
对于代码参数,详细请看下面参考文献文章,内有计算关系,具体计算占空比参数,0码与1码
按照A板手册开启定时器通道,我这里方便省事就都开了。
时钟数的配置,前面Input frequency给的25,这里给的数值可能不太对,以前都给12,但是反应过来的时候是已经弄完了,可能后面遇到的bug与这里有关。HCLK给168
PWM频率:
Fpwm =Tclk / ((arr+1)*(psc+1))(单位:Hz)
数据传送频率为800Khz,Tclk为168Mhz,设置psc = 0,arr= 210-1。
这里开启DMA通道,选择Normal模式,这里Data Width决定的是代码程序的字长,后面我遇到的问题也是与这里有些关系,导致TIM8与其他TIM的配置不同。
TIM8这里的配置与TIM2配置相同
这里我Date Width选择的是Half Word16字节的。
这里我给的是105-1,这里其实是已经有一些模糊的,因为给210-1时TIM4输出信号不对,这里我猜测可能与所挂timer clock有关。
TIM5和TIM4的配置相同。上面针对定时器的配置总体来看是有些混乱的,截图出来的是我测试出有效的结果。
下面代码大部分采用其他博主给出结果
RGB.h
#ifndef __RGB_H__
#define __RGB_H__
#include "main.h"
/*这里是计算所得CCR的宏定义,没有按照预期的计算结果,但是数据是有效的*/
#define CODE8_1 (58) //1码定时器计数次数tim8
#define CODE8_0 (25) //0码定时器计数次数tim8
/*将定时器8与其他定时器分开来,使用不同的数据,测试出来的结果*/
#define CODE_1 (30) //1码定时器计数次数
#define CODE_0 (13) //0码定时器计数次数
#define Pixel_NUM 100 //LED数量宏定义
/*建立一个定义单个LED三原色值大小的结构体*/
typedef struct
{
uint8_t R;
uint8_t G;
uint8_t B;
}RGB_Color_TypeDef;
void RGB_SetColor8(uint8_t LedId,RGB_Color_TypeDef Color);//给一个LED装载24个颜色数据码(0码和1码)
void RGB_SetColor245(uint8_t LedId,RGB_Color_TypeDef Color);//给一个LED装载24个颜色数据码(0码和1码)
void RGB_SetColorliushui245(uint8_t LedId,RGB_Color_TypeDef Color);
void RGB_SetColorliushui8(uint8_t LedId,RGB_Color_TypeDef Color);
void Reset_Load(void); //该函数用于将数组最后24个数据变为0,代表RESET_code
void RGB_RED(uint16_t pin); //显示红灯
void RGB_BLUE(uint16_t pin); //显示蓝灯
void RGB_BLACK(uint16_t pin);
void RGB_GREEN();
void MultiArrowFlowingLED(uint16_t ArrowPositions[], uint16_t ArrowCount, uint16_t Pixel_Len,uint16_t pin) ;//流水灯
void MultiArrowFlowingLEDAnimation(uint16_t pin) ;
void Sendall(uint16_t pin);//发送函数
void sunshine (uint16_t pin);//闪烁函数
#endif
RGB.c
#include "RGB.h"
#include "tim.h"
#include "control.h"
/*Some Static Colors------------------------------*/
const RGB_Color_TypeDef RED = {255,0,0}; //显示红色RGB数据
const RGB_Color_TypeDef GREEN = {0,255,0};
const RGB_Color_TypeDef BLUE = {0,0,255};
const RGB_Color_TypeDef SKY = {0,255,255};
const RGB_Color_TypeDef MAGENTA = {255,0,220};
const RGB_Color_TypeDef YELLOW = {127,216,0};
const RGB_Color_TypeDef OEANGE = {127,106,0};
const RGB_Color_TypeDef BLACK = {0,0,0};
const RGB_Color_TypeDef WHITE = {255,255,255};
/*二维数组存放最终PWM输出数组,每一行24个
数据代表一个LED,最后一行24个0代表RESET码*/
/*为了防止存储数据错乱,使用4个数组用于存放数据*/
uint32_t Pixel_Buf32[Pixel_NUM+1][24]; //tim2 4 5
uint16_t Pixel_Buf16[Pixel_NUM+1][24]; //tim8
uint32_t Pixel_Buf328[Pixel_NUM+1][24]; //tim2 4 5
uint16_t Pixel_Buf168[Pixel_NUM+1][24]; //tim8
uint8_t currentLed = 0;
/*
功能:设定单个RGB LED的颜色,把结构体中RGB的24BIT转换为0码和1码
参数:LedId为LED序号,Color:定义的颜色结构体
*/
void RGB_SetColor8(uint8_t LedId,RGB_Color_TypeDef Color)
{
uint8_t i;
if(LedId > Pixel_NUM)return; //avoid overflow 防止写入ID大于LED总数
for(i=0;i<8;i++) Pixel_Buf16[LedId][i] = ( (Color.G & (1 << (7 -i)))? (CODE8_1):CODE8_0 );//数组某一行0~7转化存放G
for(i=8;i<16;i++) Pixel_Buf16[LedId][i] = ( (Color.R & (1 << (15-i)))? (CODE8_1):CODE8_0 );//数组某一行8~15转化存放R
for(i=16;i<24;i++) Pixel_Buf16[LedId][i] = ( (Color.B & (1 << (23-i)))? (CODE8_1):CODE8_0 );//数组某一行16~23转化存放B
}
void RGB_SetColor245(uint8_t LedId,RGB_Color_TypeDef Color)
{
uint8_t i;
if(LedId > Pixel_NUM)return; //avoid overflow 防止写入ID大于LED总数
for(i=0;i<8;i++) Pixel_Buf32[LedId][i] = ( (Color.G & (1 << (7 -i)))? (CODE_1):CODE_0 );//数组某一行0~7转化存放G
for(i=8;i<16;i++) Pixel_Buf32[LedId][i] = ( (Color.R & (1 << (15-i)))? (CODE_1):CODE_0 );//数组某一行8~15转化存放R
for(i=16;i<24;i++) Pixel_Buf32[LedId][i] = ( (Color.B & (1 << (23-i)))? (CODE_1):CODE_0 );//数组某一行16~23转化存放B
}
void RGB_SetColorliushui8(uint8_t LedId,RGB_Color_TypeDef Color)
{
uint8_t i;
if(LedId > Pixel_NUM)return; //avoid overflow 防止写入ID大于LED总数
for(i=0;i<8;i++) Pixel_Buf168[LedId][i] = ( (Color.G & (1 << (7 -i)))? (CODE8_1):CODE8_0 );//数组某一行0~7转化存放G
for(i=8;i<16;i++) Pixel_Buf168[LedId][i] = ( (Color.R & (1 << (15-i)))? (CODE8_1):CODE8_0 );//数组某一行8~15转化存放R
for(i=16;i<24;i++) Pixel_Buf168[LedId][i] = ( (Color.B & (1 << (23-i)))? (CODE8_1):CODE8_0 );//数组某一行16~23转化存放B
}
void RGB_SetColorliushui245(uint8_t LedId,RGB_Color_TypeDef Color)
{
uint8_t i;
if(LedId > Pixel_NUM)return; //avoid overflow 防止写入ID大于LED总数
for(i=0;i<8;i++) Pixel_Buf328[LedId][i] = ( (Color.G & (1 << (7 -i)))? (CODE_1):CODE_0 );//数组某一行0~7转化存放G
for(i=8;i<16;i++) Pixel_Buf328[LedId][i] = ( (Color.R & (1 << (15-i)))? (CODE_1):CODE_0 );//数组某一行8~15转化存放R
for(i=16;i<24;i++) Pixel_Buf328[LedId][i] = ( (Color.B & (1 << (23-i)))? (CODE_1):CODE_0 );//数组某一行16~23转化存放B
}
/*
功能:最后一行装在24个0,输出24个周期占空比为0的PWM波,作为最后reset延时,这里总时长为24*1.2=30us > 24us(要求大于24us)
*/
void Reset_Load(void)
{
uint8_t i;
for(i=0;i<24;i++)
{
Pixel_Buf16[Pixel_NUM][i] = 0;
Pixel_Buf32[Pixel_NUM][i] = 0;
Pixel_Buf168[Pixel_NUM][i] = 0;
Pixel_Buf328[Pixel_NUM][i] = 0;
}
}
void RGB_BLACK(uint16_t cont)
{
uint16_t i;
for(i=0;i<200;i++)//给对应个数LED写入红色
{
RGB_SetColor8(i,BLACK);
RGB_SetColor245(i,BLACK);
}
Reset_Load();
Sendall(cont);
}
/*
功能:显示红色
参数:Pixel_Len为显示LED个数
*/
void RGB_RED(uint16_t cont)
{
uint16_t i;
for(i=0;i<200;i++)//给对应个数LED写入红色
{
RGB_SetColor8(i,RED);
RGB_SetColor245(i,RED);
}
Reset_Load();
Sendall(cont);
}
/*
功能:显示蓝色
参数:Pixel_Len为显示LED个数
*/
void RGB_BLUE(uint16_t cont)
{
uint16_t i;
for(i=0;i<50;i++)//给对应个数LED写入蓝色
{
RGB_SetColor8(i,BLUE);
RGB_SetColor245(i,BLUE);
}
Reset_Load();
Sendall(cont);
}
/*
功能:显示绿色
参数:Pixel_Len为显示LED个数
*/
void RGB_GREEN()
{
uint16_t i;
for(i=0;i<50;i++)//给对应个数LED写入蓝色
{
RGB_SetColor8(i,GREEN);
RGB_SetColor245(i,GREEN);
}
Reset_Load();
HAL_TIM_PWM_Start_DMA(&htim4, TIM_CHANNEL_2, (uint32_t *)Pixel_Buf32,(Pixel_NUM+1)*24);//D13 G
}
/**
* @brief 风车标杆流水灯
* @param ArrowPositions:存储每个箭头位置
ArrowCount:箭头数量
Pixel_Len:灯条长度
* @retval
*/
void MultiArrowFlowingLED(uint16_t ArrowPositions[], uint16_t ArrowCount, uint16_t Pixel_Len,uint16_t pin) {
uint16_t i;
Reset_Load();
for (i = 0; i < Pixel_Len; i++) {
uint8_t isArrow = 0; // 标志以检查当前LED是否为箭头位置
for (uint16_t j = 0; j < ArrowCount; j++) {
if (i == ArrowPositions[j] || i == ArrowPositions[j] + 5 || i == ArrowPositions[j] + 10) {
isArrow = 1;
break; // 如果是箭头,无需检查其他箭头位置
}
}
if (isArrow) {
RGB_SetColorliushui8(i, BLUE);// 设置箭头位置为红色
RGB_SetColorliushui245(i,BLUE);
} else {
RGB_SetColorliushui8(i, BLACK); // BLACK表示没有颜色(LED关闭)
RGB_SetColorliushui245(i,BLACK);
}
}
Sendall(pin);
}
/**
* @brief 风车标杆流水灯
* @param ArrowPositions:存储每个箭头位置
ArrowCount:箭头数量
Pixel_Len:灯条长度
* @retval
*/
uint16_t loopCounter = 0; // 计数器,用于限制循环次数
uint16_t maxLoops = 40;
void MultiArrowFlowingLEDAnimation(uint16_t pin) {
uint16_t frameDelay = 1; // 调整延迟时间以控制动画速度
uint16_t Pixel_Len = 30;
uint16_t totalFrames = Pixel_Len; // 循环遍历LED的总帧数(个数)
uint16_t ArrowPositions[4] = {6, 12, 18, 24};
uint16_t ArrowCount = 10;
// 设置希望执行的总循环次数,这里假设为 40 次
// 使用计数器来代替 while(1)
while (loopCounter < maxLoops) {
for (uint16_t frame = 0; frame < totalFrames; frame++) {
MultiArrowFlowingLED(ArrowPositions, ArrowCount, Pixel_Len, pin);
HAL_Delay(frameDelay);
}
// 移动箭头位置
for (uint16_t j = 0; j < ArrowCount; j++) {
ArrowPositions[j] = (ArrowPositions[j] + 1) % Pixel_Len;
}
loopCounter++; // 增加计数器
}
}
void Sendall(uint16_t pin)
{
switch (pin)
{
case 1:
HAL_TIM_PWM_Start_DMA(&htim8, TIM_CHANNEL_1, (uint32_t *)Pixel_Buf16,(Pixel_NUM+1)*24);//W
break;
case 2:
HAL_TIM_PWM_Start_DMA(&htim8, TIM_CHANNEL_2, (uint32_t *)Pixel_Buf16,(Pixel_NUM+1)*24);//I6 X
break;
case 3:
HAL_TIM_PWM_Start_DMA(&htim8, TIM_CHANNEL_3, (uint32_t *)Pixel_Buf16,(Pixel_NUM+1)*24);//I7 Y
break;
case 4:
HAL_TIM_PWM_Start_DMA(&htim8, TIM_CHANNEL_4, (uint32_t *)Pixel_Buf16,(Pixel_NUM+1)*24);//I2 Z
break;
case 5:
HAL_TIM_PWM_Start_DMA(&htim2, TIM_CHANNEL_1, (uint32_t *)Pixel_Buf32,(Pixel_NUM+1)*24);//A0 S
break;
case 6:
HAL_TIM_PWM_Start_DMA(&htim2, TIM_CHANNEL_2, (uint32_t *)Pixel_Buf328,(Pixel_NUM+1)*24);//A1 T
break;
case 7:
HAL_TIM_PWM_Start_DMA(&htim2, TIM_CHANNEL_3, (uint32_t *)Pixel_Buf328,(Pixel_NUM+1)*24);//A2 U
break;
case 8:
HAL_TIM_PWM_Start_DMA(&htim5, TIM_CHANNEL_1, (uint32_t *)Pixel_Buf328,(Pixel_NUM+1)*24);//H10 D
break;
case 9:
HAL_TIM_PWM_Start_DMA(&htim5, TIM_CHANNEL_2, (uint32_t *)Pixel_Buf328,(Pixel_NUM+1)*24);//H11 C
break;
case 10:
HAL_TIM_PWM_Start_DMA(&htim4, TIM_CHANNEL_1, (uint32_t *)Pixel_Buf328,(Pixel_NUM+1)*24);//D13 h
break;
case 11:
HAL_TIM_PWM_Start_DMA(&htim8, TIM_CHANNEL_1, (uint32_t *)Pixel_Buf16,(Pixel_NUM+1)*24);//W
HAL_TIM_PWM_Start_DMA(&htim2, TIM_CHANNEL_2, (uint32_t *)Pixel_Buf32,(Pixel_NUM+1)*24);//A1 T
break;
case 12:
HAL_TIM_PWM_Start_DMA(&htim8, TIM_CHANNEL_2, (uint32_t *)Pixel_Buf16,(Pixel_NUM+1)*24);//I6 X
HAL_TIM_PWM_Start_DMA(&htim2, TIM_CHANNEL_3, (uint32_t *)Pixel_Buf32,(Pixel_NUM+1)*24);//A2 U
break;
case 13:
HAL_TIM_PWM_Start_DMA(&htim8, TIM_CHANNEL_3, (uint32_t *)Pixel_Buf16,(Pixel_NUM+1)*24);//I7 Y
HAL_TIM_PWM_Start_DMA(&htim5, TIM_CHANNEL_1, (uint32_t *)Pixel_Buf32,(Pixel_NUM+1)*24);//H10 D
break;
case 14:
HAL_TIM_PWM_Start_DMA(&htim8, TIM_CHANNEL_4, (uint32_t *)Pixel_Buf16,(Pixel_NUM+1)*24);//I2 Z
HAL_TIM_PWM_Start_DMA(&htim5, TIM_CHANNEL_2, (uint32_t *)Pixel_Buf32,(Pixel_NUM+1)*24);//H11 C
break;
case 15:
HAL_TIM_PWM_Start_DMA(&htim4, TIM_CHANNEL_1, (uint32_t *)Pixel_Buf32,(Pixel_NUM+1)*24);//D13 h
// HAL_TIM_PWM_Start_DMA(&htim2, TIM_CHANNEL_1, (uint32_t *)Pixel_Buf32,(Pixel_NUM+1)*24);//A0 S
break;
case 16:
HAL_TIM_PWM_Start_DMA(&htim8, TIM_CHANNEL_1, (uint32_t *)Pixel_Buf16,(Pixel_NUM+1)*24);//W
HAL_TIM_PWM_Start_DMA(&htim8, TIM_CHANNEL_2, (uint32_t *)Pixel_Buf16,(Pixel_NUM+1)*24);//I6 X
HAL_TIM_PWM_Start_DMA(&htim8, TIM_CHANNEL_3, (uint32_t *)Pixel_Buf16,(Pixel_NUM+1)*24);//I7 Y
HAL_TIM_PWM_Start_DMA(&htim8, TIM_CHANNEL_4, (uint32_t *)Pixel_Buf16,(Pixel_NUM+1)*24);//I2 Z
HAL_TIM_PWM_Start_DMA(&htim2, TIM_CHANNEL_1, (uint32_t *)Pixel_Buf32,(Pixel_NUM+1)*24);//A0 S
break;
default :break;
}
}
void sunshine (uint16_t pin)
{
for(int i=0;i<=5;i++)
{RGB_BLUE(pin);
HAL_Delay (100);
RGB_BLACK (pin);
HAL_Delay (100);
}
RGB_BLUE(pin);
}
control.h
#ifndef __CONTROL_H__
#define __CONTROL_H__
void wind_control();
void wind_overall();
void wind_one();
void wind1();
void wind2();
void wind3();
void wind4();
void wind5();
void delay(int milliseconds) ;
void wind_1();
void wind_2();
void wind_3();
void wind_4();
void wind_5();
void wind_stop();
#endif
control.c
#include "control.h"
#include "Send.h"
#include "rgb.h"
#include "tim.h"
extern uint16_t loopCounter;
void delay(int milliseconds) {
// 计算循环次数
int count = 0;
// 根据 CPU 频率和循环次数计算延时时间
// 这里的延时时间是一个简单的近似值,实际延时时间可能会有误差
int loops = milliseconds * 10000;
for (count = 0; count < loops; count++) {
// 空循环
}
}
void wind_overall()
{
wind1();
wind2();
wind3();
wind4();
wind5();
wind_stop();
}
void wind_one()
{
wind_1 ();
RGB_BLACK(11);
wind_2();
RGB_BLACK(12);
wind_4 ();
RGB_BLACK(14);
wind_3();
RGB_BLACK(13);
wind_5 ();
RGB_BLACK(15);
}
void wind1()
{
wind_1();
sunshine(11);
RGB_BLUE(11);
}
void wind2()
{
wind_2();
sunshine(12);
RGB_BLUE(12);
}
void wind3()
{
wind_3();
sunshine(13);
RGB_BLUE(13);
}
void wind4()
{
wind_4();
sunshine(14);
RGB_BLUE(14);
}
void wind5()
{
wind_5 ();
RGB_BLUE(15);
sunshine(16);
}
void wind_1()
{
RGB_BLUE (1);
MultiArrowFlowingLEDAnimation(6);
loopCounter=0;
}
void wind_2()
{
RGB_BLUE (2);
MultiArrowFlowingLEDAnimation(7);
loopCounter=0;
}
void wind_3()
{
RGB_BLUE (3);
MultiArrowFlowingLEDAnimation(8);
loopCounter=0;
}
void wind_4()
{
RGB_BLUE (4);
MultiArrowFlowingLEDAnimation(9);
loopCounter=0;
}
void wind_5()
{
RGB_BLUE (5);
MultiArrowFlowingLEDAnimation(10);
loopCounter=0;
}
void wind_stop()
{
RGB_BLACK(11);
RGB_BLACK(12);
RGB_BLACK(13);
RGB_BLACK(14);
RGB_BLACK(15);
HAL_Delay(3000);
}
上面的计算0码与1码处是最大的问题,按照其他博主给的1码与0码计算方式去算结果,程序执行不起来,所以上面两处结果属于试出来的,不知道其他人能不能找到问题所在。
能量机关
参考链接:https://blog.csdn.net/qq_49519820/article/details/127930074
有其他问题大家可以私信我或者评论,本人也是个菜鸡,所以勿喷~