微控制器入門第二課-點燈大師

微控制器入門第二課-點燈大師

正式進入控制硬體的第一課

最後更新 2022/10/21 下午6:53
陈显达
預計閱讀 12 分鐘
分類
分享
標籤
硬體相關

本文由网友投稿,站長是一點都不懂硬體。

作者:陳顯達

原文標題:單晶片入門第二課----點燈大師

原文連結:https://www.cnblogs.com/1996-Chinese-Chen/p/16814553.html

引言

在上一篇文章中,我們正式開始了單晶片的學習之路,講了單晶片的概念,以及我們使用的 ESP32 系列單晶片的 IO 腳位,講了什麼是 GPIO,以及相關的匯流排通訊概念(UART、I2C、SPI),脈衝調變概念(PWM),以及訊號數位互轉的(ADC 和 DAC),板子自帶的一些功能。在今天的文章中,我會帶你們正式進入控制硬體的第一課。

不管是什麼單晶片,入門第一課都是點亮 LED 燈,俗稱「點燈大師」,哈哈,我們的第一課也是點燈。那我在群組裡沒有讓大家去購買 LED,是因為我們實際上可以使用程式碼去控制 ESP 板子上面的其中一個 LED 燈管。當電源接通後會有一個電源燈,紅色的燈亮起,表示電源正常。同時還有一個燈為藍色的,預設為不顯示。接下來我們便讓 ESP32 開發板的另外一個 LED 燈進行閃爍。

點燈

void setup() {

  // put your setup code here, to run once:
 pinMode(2,OUTPUT);
}

void loop() {
  digitalWrite(2, HIGH);   // sets the LED on
  delay(1000);                  // waits for a second
  digitalWrite(2, LOW);    // sets the LED off
  delay(1000);
}         

先上程式碼,如上所示,即可點亮板子的另一個藍色的燈。先講一下程式碼,上面的 setup,一個單晶片通電後只會執行一次,即每通電一次,執行一次。下面的 loop 是循環的程式碼,通電期間一直循環。先執行 setup,再執行 loop。上面的 setup 顧名思義,是用來對單晶片做一些配置。上面的配置就是將第二個腳位設定為輸出,即單晶片向腳位 2 輸出,輸出高低電平,用來點亮 LED 燈。在 ESP32 中,藍色燈的腳位為 2,所以在此處我們設定腳位 2 為輸出模式。

第二個 loop 循環程式碼,第一行呼叫了一個 digitalWrite 的方法,這個方法是給我們指定的腳位寫入高低電平,以此來給某個腳位開關通電、斷電。第一個參數為要寫入的腳位的 pin 值,即腳位值;第二個參數為需要寫入的值,HIGH、LOW 這兩個為高低電平,HIGH 通電,LOW 斷電。第二行程式碼則是一個延遲函式,裡面的值是毫秒值,1000 即代表暫停一秒。

下面的 gif 展示了我們這次的一個執行結果,可以看到藍色燈在不停地進行閃爍。

程式碼編譯和燒錄

在我們程式碼寫完成之後,我們需要編譯,然後再燒寫到單晶片裡面去。那每次寫完之後呢,在編輯器左上角有一個打勾的按鈕,我們點擊這個按鈕之後,IDE 會開始編譯我們的程式碼。在編譯之後,我們需要將程式碼燒錄到單晶片內部,點擊打勾旁邊的右箭頭按鈕,程式碼開始燒錄。等待下方出現 Connecting 的時候,我們要讓單晶片進入下載模式。單晶片上面有兩個按鈕,電源燈上面有一個按鈕,同橫著水平旁邊還有一個按鈕。當出現了 Connecting 的時候需要去按住下方箭頭所指的那個按鈕不鬆手,則會寫入程式。

Arduino

我們的開發 IDE 為 Arduino。我之前配過 VS Code 使用純 C 語言去進行開發 ESP32 單晶片,後面使用了 Arduino,因為這個相比於純 C 來說更簡單一些,更適合入門。但是對於 C 開發,原理也是一樣的,無非就是寫法上存在差異。

在程式執行都是不停地進行循環程式碼,但是方法上一個是 main 方法,一個是 loop,其他的就是語法的區別了。Arduino 是基於 C 和 C++ 進行封裝的,裡面的封裝更趨近於高階語言。這裡展示一下 Arduino 的一些方法或者常數、資料型別等。

C 語言

而對於 C 語言來說,有的基礎不好,或者沒有深入使用過 C 語言的人來講是有些難度的。我在這裡貼一下我之前寫的一個紅外線控制智慧小車的程式碼,此處使用的是 ESP32 原生的 C 檔案來進行開發,其複雜程度和 Arduino 相比還是略顯複雜。

/* brushed dc motor control example
   This example code is in the Public Domain (or CC0 licensed, at your option.)

   Unless required by applicable law or agreed to in writing, this
   software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
   CONDITIONS OF ANY KIND, either express or implied.
*/

/*
 * This example will show you how to use MCPWM module to control brushed dc motor.
 * This code is tested with L298 motor driver.
 * User may need to make changes according to the motor driver they use.
 */

#include <stdio.h>
#include "sdkconfig.h"
#include <string.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_attr.h"
#include "driver/rmt.h"
#include "driver/mcpwm.h"
#include "soc/mcpwm_periph.h"
#include "IR_Rev.h"
// const static char *TAG = "IR_Rre Demo";

#define RECV_PIN 23  // 一體化紅外線接收頭GPIO
uint8_t command = 0; // 接收到的ENC紅外線指令
int direction = 0;
float currentspeed = 0;
int currentcolor = 2;
#define GPIO_PWM0A_OUT 15 // Set GPIO 15 as PWM0A
#define GPIO_PWM0B_OUT 16 // Set GPIO 16 as PWM0B
#define GPIO_PWM1A_OUT 17
#define GPIO_PWM1B_OUT 18
#define GPIO_PWM2A_OUT 12 // Set GPIO 15 as PWM0A
#define GPIO_PWM2B_OUT 14 // Set GPIO 16 as PWM0B
#define GPIO_PWM3A_OUT 25
#define GPIO_PWM3B_OUT 26
#define RED 2
#define GREEN 4
#define BLUE 5
#define INFARE 21
static void mcpwm_example_gpio_initialize(void)
{
    printf("initializing mcpwm gpio...\n");
    mcpwm_gpio_init(MCPWM_UNIT_0, MCPWM0A, GPIO_PWM0A_OUT);
    mcpwm_gpio_init(MCPWM_UNIT_0, MCPWM0B, GPIO_PWM0B_OUT);
    mcpwm_gpio_init(MCPWM_UNIT_0, MCPWM1A, GPIO_PWM1A_OUT);
    mcpwm_gpio_init(MCPWM_UNIT_0, MCPWM1B, GPIO_PWM1B_OUT);
    mcpwm_gpio_init(MCPWM_UNIT_1, MCPWM0A, GPIO_PWM2A_OUT);
    mcpwm_gpio_init(MCPWM_UNIT_1, MCPWM0B, GPIO_PWM2B_OUT);
    mcpwm_gpio_init(MCPWM_UNIT_1, MCPWM1A, GPIO_PWM3A_OUT);
    mcpwm_gpio_init(MCPWM_UNIT_1, MCPWM1B, GPIO_PWM3B_OUT);
}
// forwards he forward 替换实现顺时针吹风和逆时针吹风
/**
 * @brief motor moves in forward direction, with duty cycle = duty %
 */
static void brushed_motor_forward(mcpwm_unit_t mcpwm_num, mcpwm_timer_t timer_num, float duty_cycle)
{
    mcpwm_set_signal_low(mcpwm_num, timer_num, MCPWM_OPR_A);
    mcpwm_set_duty(mcpwm_num, timer_num, MCPWM_OPR_B, duty_cycle);
    mcpwm_set_duty_type(mcpwm_num, timer_num, MCPWM_OPR_B, MCPWM_DUTY_MODE_0); // call this each time, if operator was previously in low/high state
}
static void brushed_motor_forwards(mcpwm_unit_t mcpwm_num, mcpwm_timer_t timer_num, float duty_cycle)
{
    ESP_ERROR_CHECK(mcpwm_set_signal_low(mcpwm_num, timer_num, MCPWM_OPR_B));
    ESP_ERROR_CHECK(mcpwm_set_duty(mcpwm_num, timer_num, MCPWM_OPR_A, duty_cycle));
    ESP_ERROR_CHECK(mcpwm_set_duty_type(mcpwm_num, timer_num, MCPWM_OPR_A, MCPWM_DUTY_MODE_0)); // call this each time, if operator was previously in low/high state
}
// forwards he forward 替换实现顺时针吹风和逆时针吹风
/**
 * @brief motor moves in forward direction, with duty cycle = duty %
 */
static void brushed_motor_backward(mcpwm_unit_t mcpwm_num, mcpwm_timer_t timer_num, float duty_cycle)
{
    mcpwm_set_signal_low(mcpwm_num, timer_num, MCPWM_OPR_B);
    mcpwm_set_duty(mcpwm_num, timer_num, MCPWM_OPR_A, duty_cycle);
    mcpwm_set_duty_type(mcpwm_num, timer_num, MCPWM_OPR_A, MCPWM_DUTY_MODE_0); // call this each time, if operator was previously in low/high state
}
static void brushed_motor_backwards(mcpwm_unit_t mcpwm_num, mcpwm_timer_t timer_num, float duty_cycle)
{
    ESP_ERROR_CHECK(mcpwm_set_signal_low(mcpwm_num, timer_num, MCPWM_OPR_A));
    ESP_ERROR_CHECK(mcpwm_set_duty(mcpwm_num, timer_num, MCPWM_OPR_B, duty_cycle));
    ESP_ERROR_CHECK(mcpwm_set_duty_type(mcpwm_num, timer_num, MCPWM_OPR_B, MCPWM_DUTY_MODE_0)); // call this each time, if operator was previously in low/high state
}

/**
 * @brief motor stop
 */
static void brushed_motor_stop(mcpwm_unit_t mcpwm_num, mcpwm_timer_t timer_num)
{
    mcpwm_set_signal_low(mcpwm_num, timer_num, MCPWM_OPR_A);
    mcpwm_set_signal_low(mcpwm_num, timer_num, MCPWM_OPR_B);
}

static void setspeed(uint8_t command)
{
    if ((uint8_t)command == 22)
    {
        printf("%d\n", 22);
        currentspeed = 10;
    }
    else if ((uint8_t)command == 12)
    {
        printf("%d\n", 12);
        currentspeed = 20;
    }
    else if ((uint8_t)command == 24)
    {
        printf("%d\n", 24);
        currentspeed = 30;
    }
    else if ((uint8_t)command == 94)
    {
        printf("%d\n", 94);
        currentspeed = 40;
    }
    else if ((uint8_t)command == 8)
    {
        printf("%d\n", 8);
        currentspeed = 50;
    }
    else if ((uint8_t)command == 28)
    {
        printf("%d\n", 28);
        currentspeed = 60;
    }
    else if ((uint8_t)command == 90)
    {
        printf("%d\n", 90);
        currentspeed = 70;
    }
    else if ((uint8_t)command == 66)
    {
        printf("%d\n", 66);
        currentspeed = 80;
    }
    else if ((uint8_t)command == 82)
    {
        printf("%d\n", 82);
        currentspeed = 90;
    }
    else if ((uint8_t)command == 74)
    {
        printf("%d\n", 74);
        currentspeed = 100;
    }
}
static void head(float speed)
{
    brushed_motor_forward(MCPWM_UNIT_0, MCPWM_TIMER_0, speed);
    brushed_motor_forwards(MCPWM_UNIT_0, MCPWM_TIMER_1, speed);
    brushed_motor_forward(MCPWM_UNIT_1, MCPWM_TIMER_0, speed);
    brushed_motor_forwards(MCPWM_UNIT_1, MCPWM_TIMER_1, speed);
}
static void last(float speed)
{
    brushed_motor_backward(MCPWM_UNIT_0, MCPWM_TIMER_0, speed);
    brushed_motor_backwards(MCPWM_UNIT_0, MCPWM_TIMER_1, speed);
    brushed_motor_backward(MCPWM_UNIT_1, MCPWM_TIMER_0, speed);
    brushed_motor_backwards(MCPWM_UNIT_1, MCPWM_TIMER_1, speed);
}
static void left(float speed)
{
    mcpwm_set_signal_low(MCPWM_UNIT_0, MCPWM_TIMER_1, MCPWM_OPR_A);
    mcpwm_set_signal_low(MCPWM_UNIT_1, MCPWM_TIMER_0, MCPWM_OPR_B);
}
static void right(float speed)
{
    mcpwm_set_signal_low(MCPWM_UNIT_0, MCPWM_TIMER_0, MCPWM_OPR_B);
    mcpwm_set_signal_low(MCPWM_UNIT_1, MCPWM_TIMER_1, MCPWM_OPR_A);
}
/**
 * @brief Configure MCPWM module for brushed dc motor
 */
static void mcpwm_example_brushed_motor_control(void *arg)
{
    // 1. mcpwm gpio initialization
    mcpwm_example_gpio_initialize();

    // 2. initial mcpwm configuration
    printf("Configuring Initial Parameters of mcpwm...\n");
    mcpwm_config_t pwm_config;
    pwm_config.frequency = 1000; // frequency = 500Hz,
    pwm_config.cmpr_a = 0;       // duty cycle of PWMxA = 0
    pwm_config.cmpr_b = 0;       // duty cycle of PWMxb = 0
    pwm_config.counter_mode = MCPWM_UP_COUNTER;
    pwm_config.duty_mode = MCPWM_DUTY_MODE_0;
    ESP_ERROR_CHECK(mcpwm_init(MCPWM_UNIT_0, MCPWM_TIMER_0, &pwm_config)); // Configure PWM0A & PWM0B with above settings
    mcpwm_config_t pwm_configs;
    pwm_configs.frequency = 1000; // frequency = 500Hz,
    pwm_configs.cmpr_a = 0;       // duty cycle of PWMxA = 0
    pwm_configs.cmpr_b = 0;       // duty cycle of PWMxb = 0
    pwm_configs.counter_mode = MCPWM_UP_COUNTER;
    pwm_configs.duty_mode = MCPWM_DUTY_MODE_0;
    ESP_ERROR_CHECK(mcpwm_init(MCPWM_UNIT_0, MCPWM_TIMER_1, &pwm_configs)); // Configure PWM0A & PWM0B with above settings

    mcpwm_config_t pwm_configA;
    pwm_configA.frequency = 1000; // frequency = 500Hz,
    pwm_configA.cmpr_a = 0;       // duty cycle of PWMxA = 0
    pwm_configA.cmpr_b = 0;       // duty cycle of PWMxb = 0
    pwm_configA.counter_mode = MCPWM_UP_COUNTER;
    pwm_configA.duty_mode = MCPWM_DUTY_MODE_0;
    ESP_ERROR_CHECK(mcpwm_init(MCPWM_UNIT_1, MCPWM_TIMER_0, &pwm_configA)); // Configure PWM0A & PWM0B with above settings
    mcpwm_config_t pwm_configAs;
    pwm_configAs.frequency = 1000; // frequency = 500Hz,
    pwm_configAs.cmpr_a = 0;       // duty cycle of PWMxA = 0
    pwm_configAs.cmpr_b = 0;       // duty cycle of PWMxb = 0
    pwm_configAs.counter_mode = MCPWM_UP_COUNTER;
    pwm_configAs.duty_mode = MCPWM_DUTY_MODE_0;
    ESP_ERROR_CHECK(mcpwm_init(MCPWM_UNIT_1, MCPWM_TIMER_0, &pwm_configAs)); // Configure PWM0A & PWM0B with above settings */ */
    while (1)
    {
        command = IRrecvReadIR();


        printf("IR Command is %02X\n", command);
        printf("IR 111 is %d\n", (uint8_t)command);
        if ((uint8_t)command == 69)
        {
            brushed_motor_stop(MCPWM_UNIT_0, MCPWM_TIMER_0);
            brushed_motor_stop(MCPWM_UNIT_0, MCPWM_TIMER_1);
            brushed_motor_stop(MCPWM_UNIT_1, MCPWM_TIMER_0);
            brushed_motor_stop(MCPWM_UNIT_1, MCPWM_TIMER_1);
        }
        else if ((uint8_t)command == 64)
        {
            direction = 64;
            head(currentspeed);
        }
        else if ((uint8_t)command == 25)
        {
            direction = 25;
            last(currentspeed);
        }
        else if ((uint8_t)command == 7)
        {
            printf("IR 32 is %d\n", (uint8_t)command);
            left(currentspeed);
        }
        else if ((uint8_t)command == 9)
        {
            printf("IR 23 is %d\n", (uint8_t)command);
            right(currentspeed);
        }
        else
        {
            printf(" %d\n", direction);
            setspeed(command);
            if (direction == 64)
            {
                head(currentspeed);
            }
            else if (direction == 25)
            {
                last(currentspeed);
            }
        }
    }
}
static void openlight(void *arg)
{
    gpio_set_level(INFARE, 1);
    while (1)
    {

        gpio_set_level(currentcolor, 1);
        vTaskDelay(200 / portTICK_PERIOD_MS);
        gpio_set_level(currentcolor, 0);
        if(currentcolor==2)
        {
            currentcolor=4;
        }
        else if (currentcolor==4)
        {
            currentcolor=5;
        }
        else if (currentcolor==5)
        {
            currentcolor=2;
        }
    }

}
void app_main(void)
{
    IRrecvInit(RECV_PIN, 3);
    gpio_set_direction(INFARE, GPIO_MODE_OUTPUT);
    gpio_set_direction(RED, GPIO_MODE_OUTPUT);
    gpio_set_direction(GREEN, GPIO_MODE_OUTPUT);
    gpio_set_direction(BLUE, GPIO_MODE_OUTPUT);
    xTaskCreate(mcpwm_example_brushed_motor_control, "mcpwm_examlpe_brushed_motor_control", 4096, NULL, 5, NULL);
    xTaskCreate(openlight, "openlight", 4096, NULL, 5, NULL);
}

而令我最喜歡 Arduino 的一點是,我們需要使用一些第三方函式庫的時候,這裡的擴充相比 VS Code 更容易找到一些。C 的話在擴充市集極大可能找不到,需要在網路上自己搜尋,找到 C 語言寫好的檔案,然後 include 進來再去使用,找的話有點難度。對於紅外線來講,上次我在網路上找就找了蠻長一段時間。而在 Arduino 我只需在管理函式庫介面搜尋我想要的函式庫或關鍵字都可以找到,嗯,我覺得就像 NuGet 一樣簡單,哈哈,所以我更喜歡用這個一點。

在這裡,我們就可以根據關鍵字、類型去搜尋我們想要的第三方函式庫,很方便。同時有的第三方函式庫也是有範例可以直接使用,總體來說,作為簡單入門,Arduino 還是一個很不錯的選擇。

結語

好啦,今天的點燈第一課就到這裡啦,如果還有不懂的可以隨時問我,可以加這個群一起來學習單晶片。後面還會開 STM32 系列的課程,IDE 的話可以看上一篇教學中的下載地址是有百度網盤的地址,同時群檔案也有,有需要的可以下載啦。我們系列教學會出到可以自己做一個智慧小車為止。

繼續探索

延伸閱讀

更多文章
同分類 2025/8/13

推薦一款高效能狀態機管理解決方案

在實際軟體開發中,尤其是工業軟體,每一款設備都有複雜的狀態以及狀態之間的切換的功能需求,在這種情況下,如何管理狀態以及狀態之間切換,和對應狀態下的功能控制,成為非常重要的一個問題。

繼續閱讀