#include <avr/io.h>
#include <avr/interrupt.h>
#include <stdbool.h> //在Arduino IDE中可不加 資料格式bool uint8_t等使用
// 儲存 ADC 兩軸讀值:adc_result[0] 為 X 軸,adc_result[1] 為 Y 軸
volatile uint16_t adc_result[2] = {0};
// 表示目前要轉換的 ADC 通道:0 或 1(X/Y 軸)
volatile uint8_t adc_channel = 0;
// 外部中斷旗標,記錄是否按鈕被按下
volatile bool button_pressed = false;
// Timer 計數器,用於決定何時進行 Serial 印出(0.5 秒)
volatile uint16_t print_counter = 0;
// ADC 初始化:使用 AVCC 為參考電壓、啟用中斷、設定分頻為 64
void ADC_init() {
ADMUX = (1 << REFS0);
ADCSRA = (1 << ADEN) | (1 << ADIE) | (1 << ADPS2) | (1 << ADPS1);
}
// 啟動指定通道的 ADC 轉換
void ADC_start(uint8_t channel) {
ADMUX = (ADMUX & 0xF0) | (channel & 0x0F); // 選擇通道 0 或 1
ADCSRA |= (1 << ADSC); // 開始轉換
}
// Timer2 初始化:CTC 模式,每約 10ms 中斷一次
void Timer2_init() {
TCCR2A = (1 << WGM21); // 啟用比對模式 CTC
TCCR2B = (1 << CS22) | (1 << CS21) | (1 << CS20); // 設定分頻 1024
OCR2A = 156; // 約 10ms 週期(16MHz 時鐘)
TIMSK2 = (1 << OCIE2A); // 啟用比對中斷
}
// INT0 初始化:設定下降緣觸發中斷,開啟內部上拉電阻
void INT0_init() {
EICRA |= (1 << ISC01);
EIMSK |= (1 << INT0);
DDRD &= ~(1 << PD2);
PORTD |= (1 << PD2);
}
// Timer2 中斷服務常式:每 10ms 觸發一次,啟動下一次 ADC 並累加計數器
ISR(TIMER2_COMPA_vect) {
ADC_start(adc_channel);
print_counter++;
}
// ADC 轉換完成中斷服務常式:儲存結果並切換通道
ISR(ADC_vect) {
adc_result[adc_channel] = ADC;
adc_channel = (adc_channel + 1) % 2;
}
// 外部中斷 INT0:記錄按鈕被按下的事件
ISR(INT0_vect) {
button_pressed = true;
}
// 主程式初始化
void setup() {
Serial.begin(9600);
ADC_init();
Timer2_init();
INT0_init();
sei(); // 啟用全域中斷
}
// 主程式循環:負責印出資訊與處理按鈕事件
void loop() {
// 若按鈕被按下,顯示一次資訊
if (button_pressed) {
button_pressed = false;
Serial.println("按鈕事件已處理");
}
// 每 0.5 秒印出一次 ADC 值(print_counter 到達 50)
if (print_counter >= 50) {
print_counter = 0;
Serial.print("X軸: ");
Serial.print(adc_result[0]);
Serial.print("\tY軸: ");
Serial.println(adc_result[1]);
}
if(adc_result[0] < 300){
Serial.print("下");
}
if(adc_result[0] > 700){
Serial.print("上");
}
if(adc_result[1] < 300){
Serial.print("右");
}
if(adc_result[1] > 700){
Serial.print("左");
}
}