Arduino Uno 全彩呼吸灯 三色LED灯串口控制高刷新率渐变实例 20210512更新串口中断写法,实现终极丝滑变色
点灯就是单片机界的hello world。说起点灯,我就会想三国里董卓被肚脐眼放灯芯燃烧脂肪,俗称点天灯。。。。。。。
正常的例子,这位朋友 Arduino Uno 全彩呼吸灯 三色LED灯渐变实例_凌顺实验室-CSDN博客
现在我们想用串口控制三色灯的颜色。正好试下Arduino IDE 2.0 自带代码补全。 如果不能自动补全或者出现代码提示,先下载好core库。 有时候需要保存下程序然后再重启下ide
第一个问题,串口一次只能传递一组数据,而三色灯亮需要三色参数,就需要把串口传递的参数按rgb拆成三个。(相当于自制一个通信协议)。我这里用r,g,b; 这样的形式传递一个字符串表示rgb值,例如:“255,255,0;” 表示红255绿255蓝0.
太长不看直接复制粘贴代码版:
//良好习惯之代码里不要出现让人无法理解的魔法数字
int redPin = 11; int greenPin = 10; int bluePin = 9; unsigned int red = 10; unsigned int green = 10; unsigned int blue = 10; //颜色控制函数 void color(unsigned int red,unsigned int green,unsigned int blue){ analogWrite(redPin, 255- red); analogWrite(bluePin, 255- blue); analogWrite(greenPin, 255- green); } void setup() { // put your setup code here, to run once: pinMode(redPin,OUTPUT); pinMode(greenPin,OUTPUT); pinMode(bluePin,OUTPUT); pinMode(LED_BUILTIN, OUTPUT); Serial.begin(9600); } void loop() { // put your main code here, to run repeatedly: if(Serial.available()>0) { delay(50);// 延时以确保完全接受串口数据,可以提高波特率,减小延时时间实现更高速响应 // Serial.println(Serial.readStringUntil(stopChar)); // Serial.println(Serial.readStringUntil(stopChar)); // Serial.println(Serial.readStringUntil(stopChar)); //Serial.readStringUntil可以立刻读取缓冲区数据。 red = Serial.readStringUntil(',').toInt();//注意这里是单引号表示char类型,用作函数的读取终止符号,不能用双引号,那是字符串 green = Serial.readStringUntil(',').toInt();//
Serial.readStringUntil(',')读取多少字符就会自动清理多少缓冲区里的数据
blue = Serial.readStringUntil(';').toInt();
while(Serial.read()>= 0){}//清空串口缓冲区,避免意外多余数据干扰运行
digitalWrite(LED_BUILTIN, HIGH); //这里用自带的脚13灯看程序是否运行
color(red, green, blue);
delay(5);
实际效果,用串口调试助手发送 255,0,0;三色灯立刻变红。
为什么这么写的原因(踩坑例程):一开始作为用惯了appinvnentor保姆式文本函数的人,此刻想起了被C语言字符数组指针支配的恐惧 (艹皿艹)
第一步 ,用Serial.readSring()读取一个字符串
if(Serial.available()>0) { delay(200);// 延时以确保完全接受串口数据 rgb = Serial.readString();
下一步是用符号分解字符串,作为懒人,在ide里搜索StringSplitt,还真有现成的(本来想用json,发现cJSON有点费内存)。
代码示范
StringSplitter *splitter = new StringSplitter(rgb, ',', 3);//c++中用new创建的对象返回的都是指针 red = splitter->getItemAtIndex(0);//用指针调用方法需要用->,new创建的对象不是用“*”或“.”来访问该对象的成员函数的 green = splitter->getItemAtIndex(1); blue = splitter->getItemAtIndex(2);
实际效果, 输入“255,0,0,”,灯等了一秒才变红,而且不一会程序就跑飞了(放飞自我)。
先说这个跑飞问题,单片机内存(RAM)很小,uno R3的atm328就2K(sram,静态内存,比dram快且不用刷新充电)。我把new对象写在loop()无限循环函数里,这一会就爆内存了(C++语言没有自动内存垃圾清理),所以需要添加一句 delete splitter释放内存
测试如果在水平滑动条事件里直接发送数据,会导致发送过于频繁,单片机串口数据还没接受完就又收到数据,导致灯的变色过程不正常。可以把串口波特率改高缓解这种情况。这里只用了个定时程序固定时间刷新颜色,避免数据发送太频繁。实测可以不那么丝滑的变色。
完整代码 https://pan.baidu.com/s/1mt6c53IpCUWl5apuc4USYw 提取码1234
终极更新,用串口事件来处理点灯,实测可以扛得住非常快速的发送数据.然后手机端终于可以用滑动条改变事件了,实测完全看不出来变色间隔
int redPin = 11; int greenPin = 10; int bluePin = 9; unsigned int red = 10; unsigned int green = 10; unsigned int blue = 10; //颜色控制函数 void color(unsigned int red,unsigned int green,unsigned int blue){ analogWrite(redPin, 255- red); analogWrite(bluePin, 255- blue); analogWrite(greenPin, 255- green); } void setup() { // put your setup code here, to run once: pinMode(redPin,OUTPUT); pinMode(greenPin,OUTPUT); pinMode(bluePin,OUTPUT); pinMode(LED_BUILTIN, OUTPUT); Serial.begin(9600); } void loop() {
}
void serialEvent()
{ if (Serial.available() > 0)
{ red = Serial.readStringUntil(',').toInt();
green = Serial.readStringUntil(',').toInt();
blue = Serial.readStringUntil(';').toInt();
color(red, green, blue);
}
}