แนะนำการใช้งานบอร์ด ESP32-C3 Super-Mini#
▷ ESP32-C3 Super-Mini#
บทความนี้กล่าวถึง การใช้งานบอร์ดไมโครคอนโทรลเลอร์ ESP32-C3 Super-Mini ของบริษัท QSZNTEC / Maker Go ในเบื้องต้น
ข้อมูลเกี่ยวกับบอร์ด
- ชิปตัวประมวลผล: Espressif ESP32-C3FH4 SoC
- Single-Core RISC-V 32-bit CPU, 160MHz
- IEEE 802.11 b/g/n 2.4GHz WiFi / BLE 5.0 / Bluetooth Mesh
- มีหน่วยความจำแบบแฟลชรวมไว้ในชิปเดียวกัน: Embedded SPI Flash (4MB)
- เชื่อมต่อด้วยคอนเนกเตอร์ Type-C USB
- ใช้ตัวสร้างความถี่ Crystal Oscillator 40MHz
- ไม่มีชิป USB-to-Serial Bridge ดังนั้นต้องใช้งานแบบ USB-CDC สำหรับการรับส่งข้อมูลกับคอมพิวเตอร์ของผู้ใช้
- ใช้ไอซีควบคุมแรงดันคงที่: ME611C33 LDO Voltage Regulator (3.3V)
- ใช้สายอากาศแบบ Ceramic Chip Antenna (SMD)
- มีปุ่มกด BOOT (10k pullup, GPIO-9)
- มีปุ่มกด RESET (10k pullup, CHIP_EN)
- มี Onboard LED (GPIO-8, Active-low)
- 13x I/O Pins
- 6x ADC Input Pins (A0 .. A5)
- Power-On LED: VSYS
- Power-On LED: 3.3V
- ถ้ามีการบัดกรีด้วยขา Pin Headers ก็สามารถเสียบขาลงบนเบรดบอร์ดได้ (Breadboard-Friendly)
รูป: บอร์ด ESP32-C3 Super-Mini มุมมองด้านหน้าและด้านหลัง
รูป: บอร์ด ESP32-C3 Super-Mini รุ่นเก่า (ซ้ายมือ) และรุ่นปรับปรุง (ขวามือ)
รูป: แผนผังแสดงตำแหน่งขาของบอร์ด ESP32-C3 Super-Mini (มุมมองจากด้านหลังของบอร์ด)
รูป: ผังวงจร
▷ Arduino Sketch Demo 1#
โค้ดตัวอย่างนี้สาธิตการสร้างทาสก์ (Task) โดยใช้ FreeRTOS ซึ่งรองรับการใช้งานโดยอัตโนมัติ (ถ้าใช้ Espressif ESP-IDF / Arduino Core for ESP32) ในตัวอย่างนี้เป็นการสร้างทาสก์ เพื่อทำให้ LED ที่ขา GPIO-8 กระพริบด้วยอัตราคงที่
const int ledPin = 8; // use GPIO-8
// Task function to blink the LED.
void led_blink(void *pvParameters) {
(void)pvParameters; // Task parameters not used
int state = 0;
pinMode( ledPin, OUTPUT );
while (1) {
digitalWrite( ledPin, state ^=1 ); // Toggle the LED
vTaskDelay( 500 / portTICK_PERIOD_MS );
}
}
void setup() {
Serial.begin( 115200 );
delay( 2000 );
Serial.println( "ESP32C3 FreeRTOS Demo..." );
// Create and run a FreeRTOS task.
xTaskCreate(
led_blink, // Task function
"ledBlinkTask", // Task name
512, // Stack size (words)
NULL, // Task parameter
1, // Task priority
NULL // Task handle
);
}
void loop() {
}
▷ Arduino Sketch Demo 2#
โค้ดตัวอย่างนี้สาธิตการสร้างทาสก์จำนวน 2 ทาสก์ โดยให้ทาสก์แรกอ่านค่าจากขาอินพุตแบบแอนะล็อก ตามช่วงเวลาที่กำหนดไว้ เลือกใช้ขา GPIO-2 ในตัวอย่างนี้ ค่าที่ได้จากการแปลงด้วยวงจร ADC ภายในชิป จะเป็นตัวเลขขนาด 12 บิต จากนั้นก็ส่งข้อมูลตัวเลขที่ได้ไปยังอีกทาสก์หนึ่ง โดยใช้วิธีการสื่อสารข้อมูลด้วยแถวคอย (Queue) เมื่อทาสก์ที่สองได้รับข้อมูล ก็จะนำค่าดังกล่าวไปใช้ในการอัปเดตค่า Duty Cycle ของสัญญาณ PWM เพื่อปรับความสว่างของ LED
const int AIN_PIN = 2; // GPIO-2
const int LED_PIN = 8; // GPIO-8
// FreeRTOS task handles
TaskHandle_t tasks[2];
// Queue handle for passing values between tasks.
QueueHandle_t queue = NULL;
// Task function to read analog value
void analog_input_read(void *pvParameters) {
(void)pvParameters;
pinMode( AIN_PIN, ANALOG );
analogSetPinAttenuation( AIN_PIN, ADC_11db );
analogReadResolution( 12 ); // 12-bit ADC resolution
while (1) {
// Read the analog input pin.
uint32_t value = analogRead(AIN_PIN);
// Send the analog value to the queue.
xQueueSend(queue, &value, portMAX_DELAY);
// Delay for 50 ticks (1 tick = 1 msec).
vTaskDelay( pdMS_TO_TICKS(50) );
}
}
// Task function to process the value
void led_pwm_update(void *pvParameters) {
(void)pvParameters;
int value;
while (1) {
// Wait until the queue is not empty.
if (xQueueReceive(queue, &value, portMAX_DELAY)) {
// Update the PWM duty cycle (8-bit) for LED dimming.
Serial.printf( "AIN: %lu\n", value );
analogWrite( LED_PIN, 255 - (value >> 4) );
}
}
}
void setup() {
Serial.begin( 115200 );
while(!Serial);
delay( 2000 );
Serial.println( "ESP32C3 FreeRTOS Demo..." );
// Create a FreeRTOS queue used to pass an integer value.
queue = xQueueCreate( 1, sizeof(uint32_t) );
// Create two FreeRTOS tasks
xTaskCreate(
analog_input_read, // Task function
"AnalogReadTask", // Task name
2048, // Stack size (words)
NULL, // Task input parameter
1, // Task priority
&tasks[0] // Task handle
);
xTaskCreate(
led_pwm_update, // Task function
"ledPwmTask", // Task name
4096, // Stack size (words)
NULL, // Task input parameter
2, // Task priority
&tasks[1] // Task handle
);
}
void loop() {
}
รูป: ตัวอย่างการต่อวงจรทดลองบนเบรดบอร์ด โดยใช้ตัวต้านทานปรับค่าได้สร้างสัญญาณอินพุต-แอนะล็อก
▷ Arduino Sketch Demo 3#
ตัวอย่างโค้ดถัดไป สาธิตการตรวจสอบการกดปุ่มที่ขา GPIO-9 ซึ่งเป็นปุ่มกดบนบอร์ดที่ทำงานแบบ Active-Low เมื่อมีการกดปุ่มแล้วเกิดเหตุการณ์ขอบขาลง จะทำให้เกิดอินเทอร์รัพท์ และมีการเรียกใช้ฟังก์ชัน ISR (Interrupt Service Routine) ที่เกี่ยวข้อง
ฟังก์ชัน ISR จะใช้เซมาฟอร์แบบไบนารี (Binary Semaphore) สื่อสารกับอีกทาสก์หนึ่งที่คอยอยู่ เมื่อทาสก์ดังกล่าวเข้าถึงเซมาฟอร์ได้ ก็จะสลับสถานะลอจิก LED ที่ขา GPIO-8 หนึ่งครั้ง และวนกลับไปรอเซมาฟอร์อีกรอบ
const int BTN_PIN = 9;
const int LED_PIN = 8;
// FreeRTOS task handle
TaskHandle_t buttonTask;
// FreeRTOS binary semaphore
SemaphoreHandle_t semaphore;
// Function to handle button press
void buttonAction( void *pvParameters ) {
(void)pvParameters;
bool state = 0;
uint32_t clicks = 0;
pinMode( LED_PIN, OUTPUT );
digitalWrite( LED_PIN, HIGH );
while (1) {
if (xSemaphoreTake(semaphore, portMAX_DELAY) == pdTRUE) {
digitalWrite( LED_PIN, state ^= 1 ); // Toggle the LED.
Serial.printf("Button pressed! (#%lu)\n", ++clicks );
}
}
}
void setup() {
Serial.begin( 115200 );
while(!Serial);
delay( 2000 );
Serial.println( "ESP32C3 FreeRTOS Demo..." );
pinMode( BTN_PIN, INPUT_PULLUP );
// Create a FreeRTOS binary semaphore.
semaphore = xSemaphoreCreateBinary();
// Attach an external interrupt to the button pin.
attachInterrupt( digitalPinToInterrupt(BTN_PIN), [] {
BaseType_t xHigherPriorityTaskWoken = pdFALSE;
xSemaphoreGiveFromISR( semaphore, &xHigherPriorityTaskWoken );
if (xHigherPriorityTaskWoken == pdTRUE) {
portYIELD_FROM_ISR();
}
}, FALLING );
// Create the button task.
xTaskCreate(
buttonAction, // Task function
"ButtonTask", // Task name
2048, // Stack size (words)
NULL, // Task input parameter
1, // Task priority
&buttonTask // Task handle
);
}
void loop() {
}
รูป: ตัวอย่างข้อความเอาต์พุต หลังจากมีการกดปุ่มบนบอร์ดหลายครั้ง
หากจะเปลี่ยนจากการใช้เซมาฟอร์แบบไบนารี ไปใช้วิธีการแจ้งเตือนเหตุการณ์ระหว่างทาสก์ (Task Notification) ก็มีตัวอย่างการเขียนโค้ดดังต่อไปนี้
const int BTN_PIN = 9;
const int LED_PIN = 8;
// FreeRTOS task handle
TaskHandle_t buttonTask;
// Function to handle button press
void buttonAction(void *pvParameters) {
(void)pvParameters;
bool state = 0;
uint32_t clicks = 0;
pinMode( LED_PIN, OUTPUT );
digitalWrite( LED_PIN, HIGH );
while (1) {
// Wait for task notification.
ulTaskNotifyTake( pdTRUE, portMAX_DELAY );
digitalWrite( LED_PIN, state ^= 1 ); // Toggle the LED.
Serial.printf( "Button pressed! (#%lu)\n", ++clicks );
}
}
void setup() {
Serial.begin( 115200 );
while(!Serial);
delay( 2000 );
Serial.println( "ESP32C3 FreeRTOS Demo..." );
pinMode( BTN_PIN, INPUT_PULLUP );
// Attach an external interrupt to the button pin.
attachInterrupt( digitalPinToInterrupt(BTN_PIN), [] {
// Send a task notification to the button task.
BaseType_t xHigherPriorityTaskWoken = pdFALSE;
vTaskNotifyGiveFromISR( buttonTask, &xHigherPriorityTaskWoken );
if (xHigherPriorityTaskWoken == pdTRUE) {
portYIELD_FROM_ISR();
}
}, FALLING);
// Create the button task.
xTaskCreate(
buttonAction, // Task function
"ButtonTask", // Task name
2048, // Stack size (words)
NULL, // Task input parameter
1, // Task priority
&buttonTask // Task handle
);
}
void loop() {
}
▷ Arduino Sketch Demo 4#
โค้ดตัวอย่างถัดไป สาธิตการเปลี่ยนสีของ WS2812 RGB LED เมื่อมีการกดปุ่มในแต่ละครั้ง
ตามลำดับของค่าสีที่ได้มีการกำหนดไว้ในอาร์เรย์
ในการเขียนโค้ดเพื่อใช้งานโมดูล WS2812 / NeoPixel ก็มีไลบรารี
Adafruit_NeoPixel
ของบริษัท Adafruit ให้ใช้งานได้
เมื่อมีการกดปุ่มแต่ละครั้ง จะเกิดอินเทอร์รัพท์ และฟังก์ชัน ISR ที่เกี่ยวข้องจะสื่อสารไปยังทาสก์ ด้วยวิธี Task Notification ของ FreeRTOS และทาสก์ดังกล่าวที่รออยู่ ได้รับการแจ้งเหตุการณ์จาก ISR ก็จะทำงานได้แล้วเปลี่ยนสีของ RGB LED แล้วรอการสื่อสารจาก ISR ในครั้งถัดไป
#include <Adafruit_NeoPixel.h>
const int BTN_PIN = 9;
const int WS2812_PIN = 7;
const uint32_t COLORS [] = {
0x00001f, // Blue
0x001f00, // Green
0x1f0000, // Red
0x0000000 // OFF
};
const uint32_t NUM_COLORS = sizeof(COLORS)/sizeof(uint32_t);
// Create a NeoPixel object for a single WS2812 RGB LED.
Adafruit_NeoPixel neopixel(1, WS2812_PIN, NEO_GRB + NEO_KHZ800);
// FreeRTOS task handle
TaskHandle_t buttonTask;
// Function to handle button press
void buttonAction(void *pvParameters) {
(void)pvParameters;
uint32_t clicks = 0;
uint32_t index = (NUM_COLORS-1);
neopixel.begin();
neopixel.setPixelColor(0, 0x1f1f1f );
neopixel.show();
while (1) {
// Wait for task notification.
ulTaskNotifyTake( pdTRUE, portMAX_DELAY );
Serial.printf( "Button pressed! (#%lu)\n", ++clicks );
// Change the WS2812 color.
index = (index+1) % NUM_COLORS;
neopixel.setPixelColor(0, COLORS[ index ] );
neopixel.show();
}
}
void setup() {
Serial.begin( 115200 );
while(!Serial);
delay( 2000 );
Serial.println( "ESP32C3 FreeRTOS Demo..." );
pinMode( BTN_PIN, INPUT_PULLUP );
// Attach an external interrupt to the button pin.
attachInterrupt( digitalPinToInterrupt(BTN_PIN), [] {
// Send a task notification to the button task.
BaseType_t xHigherPriorityTaskWoken = pdFALSE;
vTaskNotifyGiveFromISR( buttonTask, &xHigherPriorityTaskWoken );
if (xHigherPriorityTaskWoken == pdTRUE) {
portYIELD_FROM_ISR();
}
}, FALLING);
// Create the button task.
xTaskCreate(
buttonAction, // Task function
"ButtonTask", // Task name
2048, // Stack size (words)
NULL, // Task input parameter
1, // Task priority
&buttonTask // Task handle
);
}
void loop() {
}
รูป: ตัวอย่างการต่อวงจรทดลองบนเบรดบอร์ด โดยใช้โมดูล WS2812 RGB LED (Single-Pixel)
▷ Arduino Sketch Demo 5#
ตัวอย่างถัดไปสาธิตการรับและส่งต่อข้อมูลระหว่าง USB-CDC กับ Hardware Serial
(เลือกใช้ขา GPIP-20 สำหรับ RX และขา GPIO-21 สำหรับ TX)
มีการทำงานออกเป็นสองส่วน คือ ฟังก์ชัน task1
และ task2
task1
: เมื่อมีข้อมูลได้ถูกส่งจากคอมพิวเตอร์ของผู้ใช้ทางช่องทาง USB-CDC ก็จะมีการอ่านข้อมูลดังกล่าว แล้วส่งต่อออกไปทางขา TX ของ Hardware Serialtask2
: ถ้ามีข้อมูลถูกส่งเข้ามาทาง Hardware Serial ที่ขา RX ก็จะอ่านและส่งต่อไปยัง USB-CDC
#include <HardwareSerial.h>
#define RX_PIN (20)
#define TX_PIN (21)
#define BUF_SIZE (64)
// Create a HardwareSerial object using UART0
HardwareSerial MySerial(0);
uint8_t tx_data[ BUF_SIZE+1 ];
uint8_t rx_data[ BUF_SIZE+1 ];
void task1() {
uint32_t len;
if ( (len = Serial.available()) > 0 ) {
if ( len > BUF_SIZE ) { len = BUF_SIZE; }
// Read incoming bytes from Serial.
len = Serial.readBytes( tx_data, len );
if ( len > 0 ) {
// Send the received bytes to MySerial.
MySerial.write( tx_data, len );
}
}
}
void task2() {
uint32_t len;
if ( (len = MySerial.available()) > 0 ) {
if ( len > BUF_SIZE ) { len = BUF_SIZE; }
// Read incoming bytes from MySerial.
len = MySerial.readBytes( tx_data, len );
if ( len > 0 ) {
// Send the received bytes to Serial.
Serial.write( tx_data, len );
}
}
}
void setup() {
Serial.begin( 115200 );
Serial.setTimeout(10);
MySerial.begin( 115200, SERIAL_8N1, RX_PIN, TX_PIN );
MySerial.setDebugOutput(false);
while( MySerial.available() ) { (void)MySerial.read(); }
MySerial.setTimeout(10);
}
void loop() {
task1();
task2();
}
ถ้าจะลองเขียนให้อยู่ในรูปแบบของทาสก์สำหรับ FreeRTOS ก็มีตัวอย่างดังนี้
#include <HardwareSerial.h>
#define RX_PIN (20)
#define TX_PIN (21)
#define BUF_SIZE (64)
HardwareSerial MySerial(0);
uint8_t tx_data[ BUF_SIZE+1 ];
uint8_t rx_data[ BUF_SIZE+1 ];
void task1(void *pvParameters) {
(void)pvParameters; // Task parameters not used.
uint32_t len;
while (1) {
if ( (len = Serial.available()) > 0 ) {
if ( len > BUF_SIZE ) { len = BUF_SIZE; }
len = Serial.readBytes( tx_data, len );
if ( len > 0 ) {
MySerial.write( tx_data, len );
}
}
vTaskDelay(pdMS_TO_TICKS(10));
}
}
void task2(void *pvParameters) {
(void)pvParameters; // Task parameters not used.
uint32_t len;
while (1) {
if ( (len = MySerial.available()) > 0 ) {
if ( len > BUF_SIZE ) { len = BUF_SIZE; }
len = MySerial.readBytes( tx_data, len );
if ( len > 0 ) {
Serial.write( tx_data, len );
}
}
vTaskDelay(pdMS_TO_TICKS(10));
}
}
void setup() {
Serial.begin( 115200 );
Serial.setTimeout(10);
MySerial.begin( 115200, SERIAL_8N1, RX_PIN, TX_PIN );
MySerial.setDebugOutput(false);
while( MySerial.available() ) { (void)MySerial.read(); }
MySerial.setTimeout(10);
xTaskCreate( task1, "Task1", 1024, NULL, 1, NULL );
xTaskCreate( task2, "Task2", 1024, NULL, 1, NULL );
}
void loop() {
}
▷ กล่าวสรุป#
บทความนี้ได้นำเสนอการใช้งานบอร์ด ESP32-C3 Super-Mini ซึ่งถือว่าเป็นบอร์ดไมโครคอนโทรลเลอร์อีกตัวอย่างหนึ่งที่ใช้ชิป ESP32-C3 SoC และบอร์ดมีขนาดเล็ก ขนาดใกล้เคียงกับบอร์ด Seeed Studio XIAO ESP32-C3 นอกจากนั้นแล้วยังได้สาธิตการเขียนโค้ดด้วย Arduino + FreeRTOS เป็นตัวอย่าง
This work is licensed under a Creative Commons Attribution-ShareAlike 4.0 International License.
Created: 2023-09-17 | Last Updated: 2023-09-30