本文は読者からの寄稿であり、管理者はハードウェアについては全く詳しくありません。
著者:陳顕達
原文タイトル:マイコン入門第二課----点灯マスター
原文リンク: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);
}
まずコードを示します。上記のように記述すると、ボードのもう一つの青色LEDを点灯させることができます。コードについて説明します。setupは、マイコンに電源が投入されると一度だけ実行されます。つまり、電源投入ごとに一度だけ実行されます。loopはループするコードで、通電中は常にループします。最初にsetupが実行され、次にloopが実行されます。setupは、名前の通りマイコンの設定を行うためのものです。上記の設定では、2番目のピンを出力に設定しています。つまり、マイコンからピン2に信号を出力し、ハイまたはローレベルを出力してLEDを点灯させます。ESP32では、青色LEDのピンは2です。そのため、ここではピン2を出力モードに設定します。
2番目のloopループコードでは、最初の行でdigitalWriteメソッドを呼び出しています。このメソッドは、指定されたピンにハイまたはローレベルを書き込み、ピンのオン/オフを切り替えます。第1引数は書き込み先のピン番号、第2引数は書き込む値で、HIGH(ハイレベル)とLOW(ローレベル)があります。HIGHで通電、LOWで遮断です。2行目は遅延関数で、値はミリ秒単位です。1000は1秒間の停止を意味します。
以下のgifは、今回の実行結果を示しています。青色LEDが絶えず点滅しているのがわかります。

コードのコンパイルと書き込み
コードの記述が完了したら、コンパイルしてからマイコンに書き込む必要があります。記述が終わるたびに、エディタの左上にあるチェックマークのボタンをクリックすると、IDEがコードのコンパイルを開始します。コンパイル後、コードをマイコンに書き込む必要があります。チェックマークの隣の右矢印ボタンをクリックすると、コードの書き込みが開始されます。下部に「Connecting...」と表示されたら、マイコンをダウンロードモードにする必要があります。マイコンには2つのボタンがあります。電源ランプの上に1つのボタンがあり、その隣に水平にもう1つのボタンがあります。「Connecting...」と表示されたら、下の矢印が示すボタンを押し続けると、プログラムが書き込まれます。



Arduino
開発IDEはArduinoです。以前、VSCを使って純粋なC言語でESP32マイコンの開発を行っていましたが、その後Arduinoを使用するようになりました。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; // 受信した NEC 赤外線コマンド
int direction = 0;
float currentspeed = 0;
int currentcolor = 2;
#define GPIO_PWM0A_OUT 15 // GPIO 15 を PWM0A として設定
#define GPIO_PWM0B_OUT 16 // GPIO 16 を PWM0B として設定
#define GPIO_PWM1A_OUT 17
#define GPIO_PWM1B_OUT 18
#define GPIO_PWM2A_OUT 12 // GPIO 15 を PWM0A として設定
#define GPIO_PWM2B_OUT 14 // GPIO 16 を 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 モーターが前進方向に動きます。デューティーサイクル = 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); // オペレーターが以前に low/high 状態だった場合、毎回呼び出す
}
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)); // オペレーターが以前に low/high 状態だった場合、毎回呼び出す
}
// forwards he forward 置き換えて時計回りと反時計回りの送風を実現
/**
* @brief モーターが前進方向に動きます。デューティーサイクル = 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); // オペレーターが以前に low/high 状態だった場合、毎回呼び出す
}
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)); // オペレーターが以前に low/high 状態だった場合、毎回呼び出す
}
/**
* @brief モーター停止
*/
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 ブラシ付きDCモーター用にMCPWMモジュールを設定
*/
static void mcpwm_example_brushed_motor_control(void *arg)
{
// 1. mcpwm gpio 初期化
mcpwm_example_gpio_initialize();
// 2. mcpwm 初期設定
printf("Configuring Initial Parameters of mcpwm...\n");
mcpwm_config_t pwm_config;
pwm_config.frequency = 1000; // 周波数 = 500Hz,
pwm_config.cmpr_a = 0; // PWMxA のデューティーサイクル = 0
pwm_config.cmpr_b = 0; // 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)); // 上記設定で PWM0A & PWM0B を設定
mcpwm_config_t pwm_configs;
pwm_configs.frequency = 1000; // 周波数 = 500Hz,
pwm_configs.cmpr_a = 0; // PWMxA のデューティーサイクル = 0
pwm_configs.cmpr_b = 0; // 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)); // 上記設定で PWM0A & PWM0B を設定
mcpwm_config_t pwm_configA;
pwm_configA.frequency = 1000; // 周波数 = 500Hz,
pwm_configA.cmpr_a = 0; // PWMxA のデューティーサイクル = 0
pwm_configA.cmpr_b = 0; // 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)); // 上記設定で PWM0A & PWM0B を設定
mcpwm_config_t pwm_configAs;
pwm_configAs.frequency = 1000; // 周波数 = 500Hz,
pwm_configAs.cmpr_a = 0; // PWMxA のデューティーサイクル = 0
pwm_configAs.cmpr_b = 0; // 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)); // 上記設定で PWM0A & PWM0B を設定 */ */
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を最も気に入っている点は、サードパーティのライブラリを使用する必要がある場合、その拡張機能がVSCよりも見つけやすいことです。C言語の場合、プラグインマーケットで見つかる可能性は極めて低く、ネット上で自分で検索し、C言語で書かれたファイルを見つけてincludeしてから使用する必要があり、見つけるのに少し苦労します。赤外線に関しては、前回ネットで探すのにかなり時間がかかりました。一方Arduinoでは、管理ライブラリ画面で目的のライブラリやキーワードを検索するだけで見つけることができます。うーん、nugetのように簡単だと思います(笑)。そのため、私はこちらの方が好みです。
ここでは、キーワードやタイプに基づいて目的のサードパーティライブラリを検索でき、非常に便利です。また、一部のサードパーティライブラリには直接使用できるサンプルも付属しています。全体的に、簡単な入門としてはArduinoは非常に優れた選択肢です。


おわりに
さて、今日の点灯の第一課はここまでです。もしわからないことがあれば、いつでも質問してください。このグループに参加して、一緒にマイコンを学ぶこともできます。後ほどSTM32シリーズのコースも開講予定です。IDEについては、前回のブログのダウンロードアドレスに百度网盘のリンクがあります。また、グループのファイルにもありますので、必要な方はダウンロードしてください。シリーズチュートリアルは、自分でスマートカーを作成できるようになるまで続けます。
