1. 实验思路

可以驱动的硬件有,3个按钮,RGB 小灯,需要通过按钮切换来实现这三个功能:

  • 切换灯颜色:可以通过按钮指定灯的颜色
  • 修改灯亮度:修改当前指定的灯的颜色的亮度
  • 渐变灯:可以通过按钮控制3原色的成分

    方案1

  • 按钮1:增加当前颜色的分量

  • 按钮2:减少当前颜色的分量
  • 按钮3:确定当前颜色,设置下个颜色

    2. 硬件与插线

    2.1 按键

    按键已经在开发底板上连接,一端连接的引脚是KEY1,KEY2, KEY3,另外一端连接的都是GND
    所以当按钮摁下的时候,变为低电平。
    这时候我们需要把KEY1,KEY2,KEY3 和 旁边的 GPIO21 GPIO06 GPIO07 连接

  • GPIO21 —> KEY1

  • GPIO07 —> KEY2
  • GPIO06 —> KEY3

    image.png
    image.png
    通过杜邦线把他们连接
    image.png
    image.png

    2.2 三色灯

    三色灯在电路板上已经连接上了树莓派:

  • GPIO29 —> LED_GREEN

  • GPIO28 —> LED_RED
  • GPIO27 —> LED_BLUE

三色灯的一端是GND,另外一端是上面对应的GPIO。将三色灯模块上的RGB对应连到地板上的LEDR,LEDG,LEDB,GND接地。
image.png
image.png

3. QT + WiringPi 基础

3.1 新建项目

image.png
image.png
image.png
image.png

3.2 加入wiringPi 支持

在.pro里面, 添加:

  1. LIBS += -lwiringPi

image.png
在cpp文件的构造函数内,初始化它:

  1. #include "mainwindow.h"
  2. #include "ui_mainwindow.h"
  3. #include "wiringPi.h" // 引入头文件
  4. MainWindow::MainWindow(QWidget *parent) :
  5. QMainWindow(parent),
  6. ui(new Ui::MainWindow)
  7. {
  8. ui->setupUi(this);
  9. wiringPiSetup(); // 初始化
  10. }

4. 关键模块

4.1 按键读取

4.1.1 思路

检测按下:用定时器读取对应key引脚的GPIO,若变为低电平说明按下
检测松开: 一直等待电压变回高电平
防抖: 检测到电平变了后, 等一段时间再检测一次

4.1.2 宏定义

在mainwindow.h 里面 为三个按钮的 GPIO 定义宏:

  1. #define KEY1 21
  2. #define KEY2 07
  3. #define KEY3 06

image.png

4.1.3 相关api

  • pinmode():设置引脚模式为输入。
  • pullUpDnControl() :由于按下状态是低电平,要设置默认状态是高电平
  • digitalRead():读取引脚电平
  • delay():用于防抖的延时

4.1.4 初始化代码

初始化引脚模式并设置为高电平。因为按下接地是低电平,所以默认状态是高电平,需要通过pullUpDnControl来设置

  1. // init pin of keys
  2. pinMode(KEY1, INPUT);
  3. pinMode(KEY2, INPUT);
  4. pinMode(KEY3, INPUT);
  5. // set defalut mode is high
  6. pullUpDnControl(KEY1, PUD_UP);
  7. pullUpDnControl(KEY2, PUD_UP);
  8. pullUpDnControl(KEY3, PUD_UP);

4.1.5 定义检查按键的函数

bool isClicked(int pin)

  • 传入参数是要检测的引脚
  • 传出参数是是否被按下
    1. bool MainWindow::isClicked(int pin){
    2. if(digitalRead(pin) == 0){ // 0 是需要按下
    3. delay(100);// 防抖
    4. if(digitalRead(pin) == 0){
    5. while (digitalRead(pin) == 0);
    6. return true; // 返回的确是按下
    7. }
    8. }
    9. return false; // 返回没有是按下
    10. }

4.1.6 定时器执行的槽函数

  1. /*mainwindows.h*/
  2. private slots:
  3. void ReadKey1();
  4. void ReadKey2();
  5. void ReadKey3();
  6. /*mainwindows.cpp*/
  7. void MainWindow::ReadKey1(){
  8. if(!isClicked(KEY1)){
  9. return;
  10. }
  11. qDebug() << "key1 clicked";
  12. }
  13. void MainWindow::ReadKey2(){
  14. if(!isClicked(KEY2)){
  15. return;
  16. }
  17. qDebug() << "key2 clicked";
  18. }
  19. void MainWindow::ReadKey3(){
  20. if(!isClicked(KEY3)){
  21. return;
  22. }
  23. qDebug() << "key3 clicked";
  24. }

4.1.7 定义并初始化定时器

  1. /* mainwindows.h */
  2. public:
  3. QTimer *timer;
  4. /* mainwindows.cpp */
  5. timer = new QTimer;
  6. connect(timer, SIGNAL(timeout()), this, SLOT(ReadKey1));
  7. connect(timer, SIGNAL(timeout()), this, SLOT(ReadKey2));
  8. connect(timer, SIGNAL(timeout()), this, SLOT(ReadKey3));
  9. timer->start(50);

4.1.8 测试:

在完成以后,按下对应按键可以检测到:
image.png

4.2. PWM控制小灯

4.2.1 思路

对于单个灯,控制它在一个周期内的占比,就可以控制亮度
例如:一个周期是1000us, 亮500us, 灭500us,就是50%的亮度
一个周期是1000us, 亮800us, 灭200us,就是80%的亮度
因此有2个关键参数

  • 周期 :period
  • 占空比:duty

周期确定了,调占空比就可以调亮度
因此可以基于wiringPi的softPwm,实现

4.2.2 宏定义

  1. #define LED_R 28
  2. #define LED_G 29
  3. #define LED_B 27
  4. #define PERIOD 1000

4.2.3 用到的api

  • softPwmCreate(int pin, int val , int ranger) 设置引脚是pwm模式,参数:
    • 第一个参数是引脚
    • 第二个参数是默认值
    • 第三个参数是最可以设置的值
  • softPwmWrite(int pin, int value)
    • 第一个参数是引脚
    • 第二个参数是要设置的值(不能超过最大值)

      4.2.4 初始化

      在.pro文件里面添加 LIBS += -lpthread
      image.png
      将3个引脚都设置成PWM模式
      在.cpp 文件内,引入softPwm头文件
      image.png
      1. // init pin of led
      2. // init pin of led
      3. softPwmCreate(LED_R, 0, 255);
      4. softPwmCreate(LED_G, 0, 255);
      5. softPwmCreate(LED_B, 0, 255);

4.2.5 控制亮度

这里只展示控制亮度的示例,具体和按键的结合会在下面给出
因为范围最大是100, 所以最大值给100

  1. softPwmWrite(LED_R, 10);
  2. softPwmWrite(LED_G, 100);
  3. softPwmWrite(LED_R, 50);

5. 整体逻辑

使用一个now变量, 记录当前是哪个颜色
image.png
1 代表 红色
2 代表 绿色
3 代表 蓝色
初始值是1

5.1 颜色增加/ 减小槽函数

按钮1 是颜色增加的槽函数,首先判断当前是哪个按键,如果当前是4, 要跳回1
如果到最亮,啥都不干,不设置softPwm

  1. void MainWindow::ReadKey1(){
  2. if(!isClicked(KEY1)){
  3. return;
  4. }
  5. qDebug() << "key1 clicked";
  6. // 如果now变成了4 说明要回到1
  7. if(now == 4) now =1;
  8. switch (now) {
  9. case 1:{
  10. if(red_value==100){
  11. break; // 到达最亮
  12. }
  13. red_value += 10;
  14. softPwmWrite(LED_R, red_value);
  15. break;
  16. }
  17. case 2:{
  18. if(green_value==100){
  19. break; // 到达最亮
  20. }
  21. green_value += 10;
  22. softPwmWrite(LED_G, green_value);
  23. break;
  24. }
  25. case 3:{
  26. if(blue_value==100){
  27. break; // 到达最亮
  28. }
  29. blue_value += 10;
  30. softPwmWrite(LED_B, blue_value);
  31. break;
  32. }
  33. }
  34. }

按钮2 是颜色减少的槽函数,首先判断当前是哪个按键,如果当前是4, 要跳回1
如果到最暗,啥都不干,不设置softPwm

  1. void MainWindow::ReadKey2(){
  2. if(!isClicked(KEY2)){
  3. return;
  4. }
  5. qDebug() << "key2 clicked";
  6. qDebug() << "key1 clicked";
  7. if(now == 4) now = 1;
  8. switch (now) {
  9. case 1:{
  10. if(red_value==0){
  11. break;
  12. }
  13. red_value -= 10;
  14. softPwmWrite(LED_R, red_value);
  15. break;
  16. }
  17. case 2:{
  18. if(green_value==0){
  19. break;
  20. }
  21. green_value -= 10;
  22. softPwmWrite(LED_G, green_value);
  23. break;
  24. }
  25. case 3:{
  26. if(blue_value==0){
  27. break;
  28. }
  29. blue_value -= 10;
  30. softPwmWrite(LED_B, blue_value);
  31. break;
  32. }
  33. }
  34. }

5.3 确认槽函数

将 now + 1

  1. void MainWindow::ReadKey3(){
  2. if(!isClicked(KEY3)){
  3. return;
  4. }
  5. qDebug() << "key3 clicked";
  6. now++;
  7. }

6. GUI绑定

思路1:

  • 3个文本框, 可以设置和查看颜色分量
  • 一个确定按钮,设置颜色
  • 一个文本框,显示当前按钮的状态

image.png


7. 完整代码

头文件

  1. #ifndef MAINWINDOW_H
  2. #define MAINWINDOW_H
  3. #define KEY1 21
  4. #define KEY2 07
  5. #define KEY3 06
  6. #define LED_R 28
  7. #define LED_G 29
  8. #define LED_B 27
  9. #define PERIOD 50
  10. #include <QMainWindow>
  11. #include <QTimer>
  12. namespace Ui {
  13. class MainWindow;
  14. }
  15. class MainWindow : public QMainWindow
  16. {
  17. Q_OBJECT
  18. public:
  19. explicit MainWindow(QWidget *parent = nullptr);
  20. ~MainWindow();
  21. QTimer *timer;
  22. QTimer *led_r_timer;
  23. QTimer *led_g_timer;
  24. QTimer *led_b_timer;
  25. private:
  26. Ui::MainWindow *ui;
  27. bool isClicked(int pin);
  28. int red_value;
  29. int blue_value;
  30. int green_value;
  31. int now;
  32. private slots:
  33. void ReadKey1();
  34. void ReadKey2();
  35. void ReadKey3();
  36. };
  37. #endif // MAINWINDOW_H

cpp 文件

  1. #include "mainwindow.h"
  2. #include "ui_mainwindow.h"
  3. #include "wiringPi.h"
  4. #include "softPwm.h"
  5. #include <QDebug>
  6. MainWindow::MainWindow(QWidget *parent) :
  7. QMainWindow(parent),
  8. ui(new Ui::MainWindow)
  9. {
  10. ui->setupUi(this);
  11. wiringPiSetup();
  12. // init pin of keys
  13. pinMode(KEY1, INPUT);
  14. pinMode(KEY2, INPUT);
  15. pinMode(KEY3, INPUT);
  16. // set defalut mode is high
  17. pullUpDnControl(KEY1, PUD_UP);
  18. pullUpDnControl(KEY2, PUD_UP);
  19. pullUpDnControl(KEY3, PUD_UP);
  20. // init pin of led
  21. softPwmCreate(LED_R, 0, 100);
  22. softPwmCreate(LED_G, 0, 100);
  23. softPwmCreate(LED_B, 0, 100);
  24. //Init timer
  25. timer = new QTimer;
  26. connect(timer, SIGNAL(timeout()), this, SLOT(ReadKey1()));
  27. connect(timer, SIGNAL(timeout()), this, SLOT(ReadKey2()));
  28. connect(timer, SIGNAL(timeout()), this, SLOT(ReadKey3()));
  29. timer->start(50);
  30. red_value = 0;
  31. green_value = 0;
  32. blue_value = 0;
  33. now = 1;
  34. softPwmWrite(LED_R, 0);
  35. softPwmWrite(LED_G, 0);
  36. softPwmWrite(LED_B, 0);
  37. }
  38. MainWindow::~MainWindow()
  39. {
  40. delete ui;
  41. if(timer->isActive()){
  42. timer->stop();
  43. }
  44. delete timer;
  45. }
  46. bool MainWindow::isClicked(int pin){
  47. if(digitalRead(pin) == 0){
  48. delay(20);
  49. if(digitalRead(pin) == 0){
  50. while (digitalRead(pin) == 0);
  51. return true;
  52. }
  53. }
  54. return false;
  55. }
  56. void MainWindow::ReadKey1(){
  57. if(!isClicked(KEY1)){
  58. return;
  59. }
  60. qDebug() << "key1 clicked";
  61. if(now == 4) now =1;
  62. switch (now) {
  63. case 1:{
  64. if(red_value==100){
  65. break;
  66. }
  67. red_value += 10;
  68. softPwmWrite(LED_R, red_value);
  69. break;
  70. }
  71. case 2:{
  72. if(green_value==100){
  73. break;
  74. }
  75. green_value += 10;
  76. softPwmWrite(LED_G, green_value);
  77. break;
  78. }
  79. case 3:{
  80. if(blue_value==100){
  81. break;
  82. }
  83. blue_value += 10;
  84. softPwmWrite(LED_B, blue_value);
  85. break;
  86. }
  87. }
  88. }
  89. void MainWindow::ReadKey2(){
  90. if(!isClicked(KEY2)){
  91. return;
  92. }
  93. qDebug() << "key2 clicked";
  94. qDebug() << "key1 clicked";
  95. if(now == -1) now =3;
  96. switch (now) {
  97. case 1:{
  98. if(red_value==0){
  99. break;
  100. }
  101. red_value -= 10;
  102. softPwmWrite(LED_R, red_value);
  103. break;
  104. }
  105. case 2:{
  106. if(green_value==0){
  107. break;
  108. }
  109. green_value -= 10;
  110. softPwmWrite(LED_G, green_value);
  111. break;
  112. }
  113. case 3:{
  114. if(blue_value==0){
  115. break;
  116. }
  117. blue_value -= 10;
  118. softPwmWrite(LED_B, blue_value);
  119. break;
  120. }
  121. }
  122. }
  123. void MainWindow::ReadKey3(){
  124. if(!isClicked(KEY3)){
  125. return;
  126. }
  127. qDebug() << "key3 clicked";
  128. now++;
  129. }