STM32C8T6+RGB彩灯(驱动芯片WS28811、PWM两种调试方法)
RGB彩灯总结:
整个RGB彩灯的调试,应该花了不少于两个星期,中间遇到了很多让我很难受甚至很想放弃的problems,但是自己最终还是弄了下来,不能说是坚持了先来,但是至少能实现RGB彩灯的基本功能了,现在也在完善中。既然花了自己这么长时间,那,一定得好好记录一下。也希望大家通过我的调试过程,能够学到你们需要的东西
RGB的调试主要分了两种方法,一种是通过驱动芯片:SM16703、WS2811、TM1829三种芯片进行调试;另外一种是直接通过三个引脚,利用PWM进行调试。
两种方法其实各有优缺点:
---------------------------------------------------------------------------------
第一种方法的优点是:它只需要通过一个IO(DIN),通过给驱动芯片写入数据,就可以控制RGB彩灯的色变,但是它最大的缺点(我认为)是不管是哪个芯片,它的时序要求都是十分十分严格的,如果时序调不对,一般都是上电之后显示的是白光,当初也是因为在网上copy的代码,也没怎么详细的对代码进行分析,上来就开始调试,但是始终没有成功,又开始疯狂的在网上搜集资料,最后结论基本都是一样:调时序,时序、时序、时序是最重要的。
第二种方法,直接用PWM对R、G、B三个灯进行颜色控制,通过改变三个通道的占空比(0~255),三个灯进行颜色的组合,就可以实现不同的颜色。用这种方法虽然占用了三个IO口,但是在操作上确实是比较简单。
第一种方法,利用驱动芯片进行调试,下面以WS2811芯片为例:
WS2811芯片时序的图片。(我当时用的PE6为数据的输入引脚)
根据图表我们可以看得出:
TX0(发送0码时):
PEout(6)=1;延时为500ns;
pEout(6)=0;延时为2us;
TX1(发送1码时):
PEout(6)=1;延时为2us;
pEout(6)=0;延时为500ns;
RES设置为50us以上,我当时延时为80us;
既然延时那么重要,那我就简单的说一下调延时的思路,虽然我当时到最后也没有准确的调出来吧,但是还是想分享一下这个过程,望读者能有一些收获。
一、调ns延时函数思路:
要了解几个知识点:单片机的晶振、单片机系统时钟、时钟周期__nop()延时;
单片机的时钟:
单片机的系统时钟有几个来源(具体可以参考原子哥:第19讲 STM32时钟系统精讲),其中包括单片机内部晶振和外部晶振,STM32单片机内部和外部晶振均为8MHz,然后经过倍频、分频等一些操作成为系统时钟72MHz(可能写的不太好,也就是自己的简单了解);
上面说到了单片机的晶振,那晶振对于单片机有什么作用?
简单地说,没有晶振,就没有时钟周期,就无法执行程序代码,单片机就无法工作。STM32单片机有内部晶振和外部晶振,共同点是两个晶振都是8MHz;
不同点是:外部晶振稳定 内部晶振的误差比较大,但如果对频率要求不高的话(比如不涉及串口通信和精确定时等的话),用内部晶振就行 。但是内部时钟,频率受温度等其它影响。
时钟周期:
单片机工作时,是一条一条地从RoM中取指令,然后一步一步地执行。单片机访问一次存储器的时间,称之为一个机器周期,这是一个时间基准。—个机器周期包括12个时钟周期。如果一个单片机选择了12MHz晶振,它的时钟周期是1/12us,它的一个机器周期是12×(1/12)us,也就是1us
以STM32C8T6单片机而言:
外部晶振是8MHz,经过7倍频,为72MHz;所以它的一个时钟周期为1/72M=13.89ns
一个__nop();空语句按理来说应该占用的就是一个时钟周期,(这个可以通过逻辑分析仪进行测试)所以一个__nop();语句就是13.89ns;
这样就好说了,既然知道了一个__nop();语句占用的是13.89ns,那么当延时为500ns的时候,就可以500/13.89=36个,
// An highlighted block
void WS0_delay05us()//500ns延时
{__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();
}
//而延时2us就可以通过调用4次WS0_delay05us()函数,
void WS0_delay2us(u8 i)//当i=4时,即2us
{
while(i--)
{
WS0_delay05us();
}
}
TX0函数:
void TX0() // 发送0
{GPIO_SetBits(GPIOE, GPIO_Pin_6)WS0_delay05us();//高电平延时500nsGPIO_ResetBits(GPIOE, GPIO_Pin_6)WS0_delay2us();//低电平延时2us
}
//同理TX1也是如此,只不过高低电平的持续时间正好相反。
**注意一点的是:**如何去测试TX0的时序是否正确,方法:
把上面的的TX0放到主函数中,主函数中只执行这一条语句,然后用逻辑分析仪去抓PE6引脚,看看它的高低电平持续为多长时间。
// An highlighted block
var foo = 'bar';
以我个人的调试经验,第一次测试的时候,高低电平持续的时间肯定和标准的有很大
的差距,那么怎么办那?
一步一步地调啊,(改变__nop();的数量,或者自己也可以用系统的delay__us();函数)千万千万不要心急,当你调试不出来的时候就想想我,我当时调这个时序的时
候都快要崩溃了,2个星期,所以,不要心急,耐心。
还有一点是,当一个芯片时序实在调试不出来的时候,自己也可以去尝试其他的芯
片,因为这三种芯片在延时函数的精度要求上还是有所区别的。
给大家推荐一个我当时参考的一个视频,我觉得讲的挺不错的,也希望大家能学到一些东西。
延时函数调试链接: link.
---------------------------------------------------------------------------------
接下来给大家说一下如何用PWM直接对RGB进行调试:
原理其实很简单,改变电平的占空比使RGB分别显示出不同的色值,然后通过组合显示出想要的颜色。目前实现的功能,通过PWM调试RGB彩灯能够实现一些特定的颜色,同时也可以同串口通信、485通信发送十六进制的数据来改变RGB的颜色(其中实现过程都采用的串口调试助手进行数据的发送)。
通过网上查阅资料,我搜集到了RGB彩灯的颜色表
RGB颜色链接: link.
我用的是TIM4的三个通道PB6/PB7/PB8
代码的主要配置如下:
timer.h
// An highlighted block
#ifndef __TIMER_H
#define __TIMER_H
#include "sys.h"void TIM4_PWM_Init();
#endif
timer.c
(也就是PWM的初始化及配置),设置的重装载值为255(即0~255)正好对应RGB的颜色表。
// An highlighted block
#include "timer.h"
#include "usart.h"
#include "485.h"//TIM4 PWM部分初始化
//PWM输出初始化
//arr:自动重装值
//psc:时钟预分频数
void TIM4_PWM_Init()
{ GPIO_InitTypeDef GPIO_InitStructure;TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;TIM_OCInitTypeDef TIM_OCInitStructure;RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4, ENABLE); //使能定时器3时钟RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB|RCC_APB2Periph_AFIO, ENABLE); //使能GPIO外设和AFIO复用功能模块时钟//GPIO_PinRemapConfig(GPIO_PartialRemap_TIM3, ENABLE); //Timer3部分重映射 TIM3_CH2->PB5//设置该引脚为复用输出功能,输出TIM4 CH2的PWM脉冲波形 GPIOB.5GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6|GPIO_Pin_7|GPIO_Pin_8; //TIM_CH2GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //复用推挽输出GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOB, &GPIO_InitStructure);//初始化GPIO//初始化TIM4TIM_TimeBaseStructure.TIM_Period =255; //设置在下一个更新事件装入活动的自动重装载寄存器周期的值TIM_TimeBaseStructure.TIM_Prescaler =719; //设置用来作为TIMx时钟频率除数的预分频值TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1; //设置时钟分割:TDTS = Tck_timTIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; //TIM向上计数模式TIM_TimeBaseInit(TIM4, &TIM_TimeBaseStructure); //根据TIM_TimeBaseInitStruct中指定的参数初始化TIMx的时间基数单位//初始化TIM4 Channel/2/3/4 PWM模式TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1; //选择定时器模式:TIM脉冲宽度调制模式2TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; //比较输出使能TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_Low; //输出极性:TIM输出比较极性高//TIM_OCInitStructure.TIM_Pulse=10;TIM_OC1Init(TIM4, &TIM_OCInitStructure); //根据T指定的参数初始化外设TIM4 OC1TIM_OC2Init(TIM4, &TIM_OCInitStructure); //根据T指定的参数初始化外设TIM4 OC2TIM_OC3Init(TIM4, &TIM_OCInitStructure); //根据T指定的参数初始化外设TIM4 OC3TIM_OC4Init(TIM4, &TIM_OCInitStructure); //根据T指定的参数初始化外设TIM4 OC4TIM_OC1PreloadConfig(TIM4, TIM_OCPreload_Enable); //使能TIM4在CCR1上的预装载寄存器TIM_OC2PreloadConfig(TIM4, TIM_OCPreload_Enable); //使能TIM4在CCR2上的预装载寄存器TIM_OC3PreloadConfig(TIM4, TIM_OCPreload_Enable); //使能TIM4在CCR3上的预装载寄存器TIM_ARRPreloadConfig(TIM4,ENABLE);TIM_Cmd(TIM4, ENABLE); //使能TIM3TIM_CtrlPWMOutputs(TIM4,ENABLE);}//void RGB_SetDate(int rgb_data[])
//{
// TIM4_PWM_Init();
// TIM_SetCompare1(TIM4,rgb_data[0]);
// TIM_SetCompare2(TIM4,rgb_data[1]);
// TIM_SetCompare3(TIM4,rgb_data[2]);
//}
//*******************************************
//下面采用的是485通信进行的测试
//
//******************************************
//void RGB_SetDate()
//{
// //TIM4_PWM_Init();
// TIM_SetCompare1(TIM4,RS485_RX_BUF[0]);
// TIM_SetCompare2(TIM4,RS485_RX_BUF[1]);
// TIM_SetCompare3(TIM4,RS485_RX_BUF[2]);
//}
//下面的是利用串口通信
void RGB_SetDate()
{//TIM4_PWM_Init();TIM_SetCompare1(TIM4,USART_RX_BUF[0]);TIM_SetCompare2(TIM4,USART_RX_BUF[1]);TIM_SetCompare3(TIM4,USART_RX_BUF[2]);
}
main.c函数
// An highlighted block
#include "delay.h"
#include "key.h"
#include "sys.h"
#include "timer.h"
#include "usart.h"
#include "485.h"
int red[3]={255,0,0};
int yellow[3]={255,255,0};
int green[3]={0,255,0};
int blue[3]={0,0,25500};
int qin[3]={0,255,255};
//int zi[3]={160,32,240};
int ziluolan[3]={138,43,226};
int hei[3]={0,0,0}; int main(void){ u8 key;u16 t; NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //设置NVIC中断分组2:2位抢占优先级,2位响应优先级uart_init(9600);delay_init(); //延时 函数初始化TIM4_PWM_Init();RS485_Init(9600); //初始化RS485 初始化串口的两个引脚PA2 PA3和PD7(485使能引脚)while(1) { //**********下面采用的是485进行调试
// RS485_Send_Data(RS485_RX_BUF,5);
// RS485_Receive_Data(RS485_RX_BUF,&key);
// if(key)//接收到有数据
// {
// if(key>5)key=5;//最大是5个数据.
// }
//**********采用的是485进行调试
//----------------------------------------------------------
//----------------------------------------------------------
//**********下面采用的是串口进行调试if(USART_RX_STA&0x8000){ for(t=0;t<3;t++){USART_SendData(USART1, USART_RX_BUF[t]);//向串口1发送数据while(USART_GetFlagStatus(USART1,USART_FLAG_TC)!=SET);//等待发送结束}USART_RX_STA=0;//清零语句是X必须有的,就像哪个输入捕获实验,如果不清零就不会再执行中断函数//RGB_SetDate(USART_RX_BUF);}
//***************采用的是串口进行调试RGB_SetDate(USART_RX_BUF);//RGB_SetDate(RS485_RX_BUF);
// delay_ms(500);
// RGB_SetDate(yellow);
// delay_ms(500);
// RGB_SetDate(green);
// delay_ms(500);
// RGB_SetDate(blue);
// delay_ms(500);
// RGB_SetDate(qin);
// delay_ms(500);
// RGB_SetDate(hei);
// delay_ms(500);
// RGB_SetDate(ziluolan);
// delay_ms(500);} }
嗯,最后放一下两个星期的调试成功吧:
RGB彩灯调试
代码链接:
链接:https://pan.baidu.com/s/1d4H_htjQOsrc5Wan0QwTuw
提取码:4yau
哈哈哈哈,不管怎么样,还是调试出来了,高兴,不过还需要继续完善。
//------------------------------------------------------------------------------------------
目前的问题:RGB电压的供电问题(目前是7.98V)控制1米的灯带,以及利用蓝牙通信(串口通信)时,只能收到一次数据的问题
STM32C8T6+RGB彩灯(驱动芯片WS28811、PWM两种调试方法)相关推荐
- android app两种调试方法
方法一: 1.使用apktool的-d选项反编译apk文件 java -jar apktool.jar d -d target.apk -o output 2.在AndroidManif ...
- 快速排序的两种实现方法(c语言版本)
经过调研发现,对任意无序整数数组,快速排序有两种实现方法,这里简单阐述下思路: 思路一:随意选择一个基准元,一般选择数组的起始元或末尾元,Weiss这本书上特意搞了个算法来选择基准元,--,总之就是基 ...
- R语言生存分析COX回归分析实战:两种治疗方法发生肾功能损害的情况
R语言生存分析COX回归分析实战:两种治疗方法发生肾功能损害的情况 目录
- mysql workbench kernelbase.dll_电脑出现kernelbase.dll错误的两种解决方法
KernelBase.dll是Windows操作系统的重要文件,它为各种应用程序提供服务.如果电脑提示kernelbase.dll错误,这该怎么处理?大家可以用电脑自带的防火墙或者是第三方软件来进行故 ...
- 使用定制的NSDictionary的方法,对NSArray进行排序(附:数组排序两种常见方法)
NSArray中存放的是NSDictionary,可以使用策略的方法对NSDictionary进行定制,增加比较的方法.然后调用NSArray的sortUsingSelector方法对数组进行排序,这 ...
- Ext.Ajax.request和formPanel.getForm().submit()两种提交方法的异同:
Ext.Ajax.request和formPanel.getForm().submit()两种提交方法的异同: 1. 相同点: a) 都是使用异步提交的方式: b) 默认都是使用POST方式来提交数据 ...
- java代码二进制转为十六进制_Java 中二进制转换成十六进制的两种实现方法
Java 中二进制转换成十六进制的两种实现方法 每个字节转成16进制,方法1 /** * 每个字节转成16进制,方法1 * * @param result */ private static Stri ...
- python ioc di_Spring介绍,IOC(控制反转),DI(依赖注入)介绍及两种注入方法
Spring介绍,IOC(控制反转),DI(依赖注入)介绍及两种注入方法 第一中方法:在xml文件中注入: (1)开源的轻量级的应用开发框架 特点:a.简化开发:b.解耦:c.集成: 原理对象与对象之 ...
- Json返回时间中出现乱码问题的两种解决方法
Json返回时间中出现乱码问题的两种解决方法 参考文章: (1)Json返回时间中出现乱码问题的两种解决方法 (2)https://www.cnblogs.com/hanyinglong/archiv ...
最新文章
- ubuntu下安装flex和bison
- linux 查看日志信息--less命令
- 滴滴高管今年集体不拿年终奖 员工奖励力度缩减一半
- 小鱼易连电脑版_电脑?不,它是随时就绪的专业电话会议解决方案
- java 树 右键菜单_jQuery实现自定义右键菜单的树状菜单效果
- zune自搭虚拟服务器离线升级,Zune 30g 固件更新至 3.3 记录
- 视觉中的经典图像特征小结(一): 颜色直方图, HOG, LBP
- 【牛客 - 318L】彪神666(水题,半高精度,递推,trick)
- cad一键标注闭合区域lisp_CAD快捷键大全,你值得学会!
- Linux下实现Raid 5软阵列
- python | 实现多行向量(matrix)两两计算余弦距离、欧几里德距离
- SAP License:会计->用友->金蝶->SAP,我的工作历程
- Gradle_04_解决多项目同级依赖时找不到符号的异常
- 别等找工作时才明白:程序员只会敲代码是不行的!不看后悔!
- 开张第一天,一年之际在于春
- 十五、K8s helm包管理与应用
- 大学生计算机应用论文,大学生计算机应用论文(共1178字).doc
- ftp客户端flashfxp破解教程
- dotnet core在Linux下运行的步骤
- 关于FPGA软件quartus仿真出现cannot launch the modelsim software问题的解决
热门文章
- oracle 存储过程误删,oracle恢复误删的procedure存储过程
- NGUI发布后看不见UI层解决
- 计算机毕设 SpringBoot智慧外贸平台系统 外贸商城 代购商城 海外代购平台Java Vue MySQL数据库 远程调试 代码讲解
- Python3网络爬虫开发实战,Scrapy 爬取新浪微博
- 最快引流的10个引流方法
- layer.open使用方法和参数
- Linux常用命令及软件更新
- 【无人机】基于SDRE对NPS II无人机进行点对点(调节)控制(Matlab代码实现)
- .net 导出Excel,设置Excel页眉及单元格换行方法
- 2009年5月26日