คำนำ

ในบทช่วยสอนแบบฝังครั้งล่าสุด ฉันบอกว่าถ้าคุณต้องการพัฒนาแบบฝังอย่างจริงจังคุณต้องลืมเรื่องฮาร์ดแวร์ไปสักหน่อย เพื่อที่คุณจะได้สามารถเข้าถึงอย่างเต็มที่เพื่อเรียนรู้โครงสร้างระดับต่ำเพื่อ พัฒนารากฐานที่มั่นคงในด้านนี้

ในบทช่วยสอนนี้ ปฏิบัติตามบรรทัดการไม่ใช้ฮาร์ดแวร์นามธรรมและ IDE

เครื่องมือที่คุณต้องการ

เมื่อเปรียบเทียบกับบทช่วยสอนครั้งก่อน คุณจะต้องมีอุปกรณ์เพิ่มขึ้นอีกเล็กน้อย :

  1. บอร์ด Arduino พร้อมสายเคเบิล
  2. โพเทนชิออมิเตอร์
  3. นำ
  4. ตัวต้านทาน 220 โอห์ม (ขั้นต่ำ)
  5. สายจัมเปอร์บางอัน

ความคิด

เราจะเห็นความสว่างของไฟ LED ได้อย่างไร? โดยปกติแล้วสัญญาณดิจิทัลอาจเป็น 0V หรือ 5V ซึ่งหมายถึงเปิดหรือปิด เป็นไปได้ไหมที่จะเห็นสเปกตรัมความสว่างระหว่างแรงดันไฟฟ้านี้?

ด้วยสัญญาณดิจิตอลไม่แน่นอน

หากเป็นไปไม่ได้ เราจะทำให้โครงการนี้เกิดขึ้นได้อย่างไร?

ใจเย็นๆนะเด็กน้อย ให้ฉันอธิบาย.

โชคดีที่เราใช้ชีวิตอยู่ในคำที่คล้ายคลึงกัน ซึ่งสิ่งต่างๆ ไม่ใช่แค่เปิดหรือปิดเท่านั้น ดังนั้นเราจึงสามารถรับสัญญาณอะนาล็อกเพื่อควบคุมความสว่างของไฟ LED ได้

แต่เราจะสร้างความตึงเครียดแบบแปรผันเพื่อควบคุมไฟ LED ได้อย่างไร?

คำถามที่ดี! นั่นคือตอนที่โพเทนชิออมิเตอร์เข้ามาแทนที่ มันจะเป็นผู้รับผิดชอบในการสร้างความตึงเครียดที่แปรผันนี้

โพเทนชั่นเอ็มเตอร์

นี่คือเครื่องมือหลักที่เราจะต้องสร้างความตึงเครียดระหว่าง 0V ถึง 5V

ทำไม

เนื่องจากโพเทนชิออมิเตอร์เป็นเพียงความต้านทานแบบแปรผัน เราจึงสามารถสร้างตัวแบ่งแรงดันไฟฟ้าแบบแปรผันได้ ซึ่งเป็นวงจรคลาสสิกจากคลาสอิเล็กทรอนิกส์ของคุณ แผนภาพวงจรมีดังนี้:

ตัวแปลงอนาล็อกเป็นดิจิทัล (ADC) : สะพานเชื่อมระหว่างสองโลก

เราจะใช้โพเทนชิออมิเตอร์เพื่อปรับความเข้มของแสง แต่ใครบอกว่าคอมพิวเตอร์สามารถเข้าใจสัญญาณประเภทนี้ได้ ในบทความนี้ ฉันอธิบายว่าทำไมคอมพิวเตอร์ไม่เข้าใจสิ่งนี้

ดังนั้น เพื่อให้สัญญาณนี้เป็นสัญญาณที่เหมาะสมเพื่อให้โปรเซสเซอร์สามารถเข้าใจและใช้สัญญาณได้ นั่นคือเวลาที่ ADC เข้ามา

มันเป็นสะพานเชื่อมระหว่างโลกแห่งความจริง ทางกายภาพ และเชิงอะนาล็อกกับโลกดิจิทัล

ดังนั้นด้วย และ ADC เรามีสัญญาณดิจิทัลที่ CPU สามารถประมวลผลได้

Pulse-Width Modulation (PWM) : ตัวควบคุมแสงจริง

โอเค ตอนนี้เรามีสัญญาณดิจิทัลแล้ว จะส่งไปยัง LED เพื่อควบคุมความสว่างได้อย่างไร? ใจเย็นๆ นะ มันไม่ได้ผลแบบนั้นหรอก

นั่นเป็นเพราะว่า LED ทำงานแตกต่างไปจากหลอดไฟแบบเดิม เป็นต้น ต่างจากหลอดไฟที่จะสว่างขึ้นเมื่อคุณส่งพลังงานมากขึ้น LED มีแรงดันไฟฟ้าตกคงที่พาดผ่าน และหากคุณส่งพลังงานมากเกินไป คุณสามารถสร้างความเสียหายหลอดไฟได้

และนั่นคือจุดที่ PWM เข้ามา พูดง่ายๆ ก็คือ PWM คือ เทคนิคในการควบคุมพลังงานที่ส่งไปยังอุปกรณ์ด้วยการเปิดและปิดอย่างรวดเร็ว (เราสามารถพูดคุยเพิ่มเติมเกี่ยวกับหัวข้อนี้ในบทความอื่น)

และนั่นเป็นความลับเบื้องหลัง "การควบคุม" ความสว่างของ LED ความจริงก็คือความสว่างไม่ได้ถูกควบคุม สิ่งที่มันทำคือการเปิดและปิดอย่างรวดเร็วมากจนดูเหมือนกำลังหรี่ลง

นั่นคือสิ่งที่ PWM ทำ โดยจะเปิดและปิด LED อย่างรวดเร็ว และด้วยการเปลี่ยนแปลงระยะเวลาของเวลา "เปิด" เทียบกับเวลา "ปิด" ก็สามารถสร้างภาพลวงตาของการหรี่แสงได้

ขออภัยที่ทำให้คุณผิดหวัง แต่มันเป็นเพียงภาพลวงตา

อย่างไรก็ตาม หลังจากอธิบายส่วนต่าง ๆ แล้ว เรามาเข้าเรื่องโปรเจ็กต์กันดีกว่า

การตั้งค่า

การตั้งค่าจะทำจากเครื่องมือที่กล่าวถึงในตอนต้นของบทความนี้ นี่คือรูปภาพของมัน:

ฉันรู้ว่ามันแน่นไปหน่อย แต่มาสรุปวิธีประกอบชิ้นส่วนกัน:

  1. เชื่อมต่อบอร์ด Arduino เข้ากับคอมพิวเตอร์ของคุณโดยใช้สาย USB
  2. วาง LED และโพเทนชิออมิเตอร์ไว้บนเขียงหั่นขนม เชื่อมต่อขั้วบวกของ LED เข้ากับพินดิจิทัล 9 (หรือพินดิจิทัลอื่น ๆ ) บนบอร์ด Arduino เชื่อมต่อแคโทดของ LED กับตัวต้านทาน 220 โอห์ม (หรือสูงกว่า) จากนั้นเชื่อมต่อปลายอีกด้านของตัวต้านทานเข้ากับกราวด์บนเขียงหั่นขนม
  3. เชื่อมต่อพินกลางของโพเทนชิออมิเตอร์เข้ากับพินอะนาล็อก 0 (หรือพินอะนาล็อกอื่น ๆ ) บนบอร์ด Arduino เชื่อมต่อพินด้านนอกอันหนึ่งของโพเทนชิออมิเตอร์เข้ากับ 5V บนเขียงหั่นขนม และเชื่อมต่อพินด้านนอกอีกอันเข้ากับกราวด์
  4. เชื่อมต่อสายจัมเปอร์จาก 5V บนเขียงหั่นขนมเข้ากับอีกด้านของตัวต้านทาน 220 โอห์ม
  5. สร้างโค้ด C ของคุณแล้วดำเนินการ

ทฤษฎีมากเกินไป มาทำให้มือของเราสกปรกกันเถอะ

นี่เป็นทฤษฎีมากเกินไป เรามาดูส่วนที่ดีที่สุดกันดีกว่า

รหัส C เพื่อทำให้ทั้งหมดนี้เกิดขึ้น

มาดูโค้ดกัน:

#include <avr/io.h>
#include <util/delay.h>

#define LED_PIN PB1
#define POT_PIN PC0

void init_led() {
    DDRB |= (1 << LED_PIN);
    PORTB &= ~(1 << LED_PIN);
}

void init_adc() {
    ADMUX |= (1 << REFS0);
    ADCSRA |= (1 << ADEN) | (1 << ADPS2) | (1 << ADPS1);
}

int read_adc() {
    ADCSRA |= (1 << ADSC);
    while (ADCSRA & (1 << ADSC));
    return ADC;
}

void init_pwm() {
    TCCR1A = (1 << COM1A1) | (1 << WGM11);
    TCCR1B = (1 << WGM13) | (1 << WGM12) | (1 << CS11);
    ICR1 = 255;
    OCR1A = 0;
}

int main(void) {
    init_led();
    init_adc();
    init_pwm();

    while (1) {
        int pot_value = read_adc();
        int brightness = pot_value >> 2;

        OCR1A = brightness;

        _delay_ms(100);
    }

    return 0;
}

มาวิเคราะห์โค้ดนี้ด้วยกัน แต่ก่อนหน้านั้น : หากคุณไม่เห็นบทช่วยสอนโปรเจ็กต์แบบฝังครั้งแรกของฉันด้วย Arduino โปรดอ่าน ที่นี่ ฉันอธิบายส่วนพื้นฐาน เช่น การรวมส่วนหัวและฟังก์ชันหลัก

#define LED_PIN PB1
#define POT_PIN PC0

สองบรรทัดนี้ใช้ คำสั่งตัวประมวลผลล่วงหน้า #define เพื่อสร้างค่าคงที่สำหรับหมายเลขพินที่จะใช้สำหรับพินดิจิทัลของ LED และพินแอนะล็อกของโพเทนชิออมิเตอร์

void init_led() {
    DDRB |= (1 << LED_PIN);
    PORTB &= ~(1 << LED_PIN);
}

ฟังก์ชันนี้เริ่มต้นพิน LED โดยการตั้งค่าบิตที่เกี่ยวข้องในรีจิสเตอร์ DDRB เป็น 1 ซึ่ง กำหนดค่าให้เป็นพินเอาท์พุต และโดยการตั้งค่าบิตที่สอดคล้องกันในรีจิสเตอร์ PORTB ให้เป็น 0 ซึ่งจะตั้งค่าพิน ไปสู่สถานะต่ำ ฉันอธิบายเพิ่มเติมเกี่ยวกับการลงทะเบียนเหล่านั้นในบทความบทช่วยสอนแบบฝังตัวแรกของฉัน

void init_adc() {
    ADMUX |= (1 << REFS0);
    ADCSRA |= (1 << ADEN) | (1 << ADPS2) | (1 << ADPS1);
}

ฟังก์ชันนี้เริ่มต้น ADC โดย การตั้งค่าแรงดันไฟฟ้าอ้างอิง เพื่อใช้ Vcc เป็นแรงดันอ้างอิง เปิดใช้งาน ADC โดยการตั้งค่าบิตที่สอดคล้องกันในรีจิสเตอร์ ADCSRA เป็น 1 และตั้งค่า พรีสเกลเลอร์ ADC เป็น 64 .

int read_adc() {
    ADCSRA |= (1 << ADSC);
    while (ADCSRA & (1 << ADSC));
    return ADC;
}

ฟังก์ชันนี้จะอ่านค่าแอนะล็อกจากโพเทนชิออมิเตอร์โดยการตั้งค่าบิตการแปลงเริ่มต้นของ ADC ในรีจิสเตอร์ ADCSRA เป็น 1 และรอให้การแปลงเสร็จสมบูรณ์โดยรอเป็นลูปในขณะที่ ADC ยังคงยุ่งอยู่ เมื่อการแปลงเสร็จสมบูรณ์ ฟังก์ชันจะส่งกลับค่า 10 บิตที่จัดเก็บไว้ในรีจิสเตอร์ ADC

void init_pwm() {
    TCCR1A = (1 << COM1A1) | (1 << WGM11);
    TCCR1B = (1 << WGM13) | (1 << WGM12) | (1 << CS11);
    ICR1 = 255;
    OCR1A = 0;
}

ฟังก์ชันนี้เริ่มต้นเอาต์พุต PWM โดยการตั้งค่าโหมดการสร้างรูปคลื่นเป็น PWM ที่รวดเร็วโดยมีค่าสูงสุด 0xFF เปิดใช้งานเอาต์พุต PWM บนพิน OCR1A ตั้งค่าพรีสเกลเลอร์ PWM เป็น 8 และตั้งค่ารอบการทำงานเริ่มต้น ถึง 0

int main(void) {
    init_led();
    init_adc();
    init_pwm();

    while (1) {
        int pot_value = read_adc();
        int brightness = pot_value >> 2;

        OCR1A = brightness;

        _delay_ms(100);
    }

    return 0;
}

นี่คือหน้าที่หลักของโปรแกรม โดยเรียกฟังก์ชันการเริ่มต้นทั้งสามฟังก์ชัน จากนั้นเข้าสู่ลูปอนันต์ที่ อ่านค่าโพเทนชิออมิเตอร์ ลดขนาดลงเป็นค่า 8 บิตโดยการเลื่อนบิตไปทางขวาทีละสองบิต ตั้งค่ารอบการทำงานของเอาต์พุต PWM เป็นค่าที่ปรับขนาด จากนั้นรอ 100 มิลลิวินาทีก่อนที่จะวนซ้ำ

และฉันจะรู้ชื่อทะเบียนแปลก ๆ เหล่านี้ได้อย่างไร? เอกสารข้อมูล

เมคไฟล์

MCU = atmega328p
F_CPU = 16000000UL
PROGRAMMER_TYPE = arduino
PORT = /dev/ttyACM0
HEXFILE = main.hex
SOURCES = main.c
OBJECTS = $(SOURCES:.c=.o)
CFLAGS = -mmcu=$(MCU) -DF_CPU=$(F_CPU) -Wall -Os
AVRDUDE = avrdude

.PHONY: all clean install

all: $(HEXFILE)

$(HEXFILE): $(OBJECTS)
 avr-gcc $(CFLAGS) -o $@ $(OBJECTS)
 avr-objcopy -O ihex $@ $(HEXFILE)

$(OBJECTS): $(SOURCES)
 avr-gcc $(CFLAGS) -c $(SOURCES)

clean:
 rm -f $(OBJECTS) $(HEXFILE)

install: $(HEXFILE)
 $(AVRDUDE) -p $(MCU) -c $(PROGRAMMER_TYPE) -P $(PORT) -b 115200 -D -U flash:w:$(HEXFILE)

มาดูไฟล์ make นี้กัน

MCU = atmega328p
F_CPU = 16000000UL
PROGRAMMER_TYPE = arduino
PORT = /dev/ttyACM0
HEXFILE = main.hex
SOURCES = main.c
OBJECTS = $(SOURCES:.c=.o)
CFLAGS = -mmcu=$(MCU) -DF_CPU=$(F_CPU) -Wall -Os
AVRDUDE = avrdude

ขั้นแรก เรากำหนดตัวแปรของเรา:

  1. MCU = atmega328p: กำหนดไมโครคอนโทรลเลอร์เป้าหมายเป็น ATmega328P
  2. F_CPU = 16000000UL : ตั้งค่าความถี่สัญญาณนาฬิกาเป็น 16 MHz
  3. PROGRAMMER_TYPE = arduino : ระบุประเภทโปรแกรมเมอร์เป็น “arduino”
  4. PORT = /dev/ttyACM0 : ระบุพอร์ตอนุกรมสำหรับโปรแกรมเมอร์
  5. HEXFILE = main.hex : ตั้งชื่อไฟล์เอาต์พุตเป็น “main.hex”
  6. SOURCES = main.c : ระบุไฟล์ซอร์สโค้ดเป็น “main.c”
  7. OBJECTS = $(SOURCES:.c=.o) : ระบุไฟล์อ็อบเจ็กต์เป็น “main.o”
  8. CFLAGS = -mmcu=$(MCU) -DF_CPU=$(F_CPU) -Wall -Os : ระบุแฟล็กคอมไพเลอร์ รวมถึงไมโครคอนโทรลเลอร์ ความถี่สัญญาณนาฬิกา ระดับคำเตือน และระดับการปรับให้เหมาะสม
  9. AVRDUDE = avrdude : ระบุคำสั่ง avrdude ที่จะใช้สำหรับการอัปโหลดโค้ดไปยังไมโครคอนโทรลเลอร์
.PHONY: all clean install

บรรทัดนี้ระบุเป้าหมายที่ไม่ใช่ไฟล์จริง แต่เป็นเพียงคำสั่งที่จะดำเนินการ

all: $(HEXFILE)

บรรทัดนี้ระบุเป้าหมายเริ่มต้น ที่จะสร้าง ซึ่งขึ้นอยู่กับไฟล์ hex

$(HEXFILE): $(OBJECTS)
    avr-gcc $(CFLAGS) -o $@ $(OBJECTS)
    avr-objcopy -O ihex $@ $(HEXFILE)

บรรทัดนี้ระบุวิธีสร้างไฟล์ hex จากไฟล์อ็อบเจ็กต์โดยใช้คำสั่ง avr-gcc และ avr-objcopy

$(OBJECTS): $(SOURCES)
    avr-gcc $(CFLAGS) -c $(SOURCES)

บรรทัดนี้ระบุวิธีสร้างไฟล์อ็อบเจ็กต์จากไฟล์ต้นฉบับโดยใช้คำสั่ง avr-gcc

clean:
    rm -f $(OBJECTS) $(HEXFILE)

บรรทัดนี้ระบุวิธีล้างออบเจ็กต์และไฟล์ hex โดยใช้คำสั่ง rm

install: $(HEXFILE)
    $(AVRDUDE) -p $(MCU) -c $(PROGRAMMER_TYPE) -P $(PORT) -b 115200 -D -U flash:w:$(HEXFILE)

บรรทัดนี้ระบุวิธีอัปโหลดไฟล์ hex ไปยังไมโครคอนโทรลเลอร์โดยใช้ avrdude

ตอนนี้คุณพร้อมที่จะดำเนินการแล้ว คุณสามารถเขียน :

make

ติดตามโดย

make install

ผลลัพธ์สุดท้าย

ขออภัย ฉันไม่สามารถวางวิดีโอของฉันที่แสดงการทำงานของโปรเจ็กต์ได้ที่นี่

เร็วๆ นี้ ฉันจะให้ลิงก์ไปยัง Instagram ของฉัน (หากคุณต้องการดูเนื้อหารายวันเกี่ยวกับระบบฝังตัว ติดตามฉัน ที่นี่) เพื่อให้คุณดูวิดีโอได้

ยังไงก็ทดสอบได้ด้วยตัวเอง

และเช่นเคย ขอขอบคุณที่สละเวลาอ่านฉัน!

บทความเพิ่มเติม

  1. ความจริงอันโหดร้ายที่ต้องรับมือหากคุณกำลังเริ่มการพัฒนาซอฟต์แวร์แบบฝังตัว
  2. ฝึกฝนพื้นฐานของระบบฝังตัวด้วยคำถามที่ต้องถาม 2 ข้อนี้