#include <avr/io.h>
#include <util/delay.h>
#define SS PB2 // Slave Select (ST_CP)
#define MOSI PB3 // Master Out Slave In (DS)
#define SCK PB5 // Serial Clock (SH_CP)
#define flash_time 50
#define flash_persec 1000/flash_time
#define pause_time 100
int num = 0, count = 0;
volatile bool paused = false; // false=正常計數, true=暫停閃爍
volatile int blink = 0; // 閃爍狀態 0=暗 1=亮
volatile uint8_t digit = 0; // 0=個位, 1=十位
const uint8_t digit_codes[10] = {
0b11111100, // 0
0b01100000, // 1
0b11011010, // 2
0b11110010, // 3
0b01100110, // 4
0b10110110, // 5
0b10111110, // 6
0b11100000, // 7
0b11111110, // 8
0b11110110 // 9
};
// SPI 初始化 (Master 模式)
void SPI_init() {
DDRB |= (1 << MOSI) | (1 << SCK) | (1 << SS); // 設定 MOSI, SCK, SS 為輸出
SPCR = (1 << SPE) | (1 << MSTR) | (1 << SPR0); // 啟用 SPI, 設為 Master 模式
}
// 傳送一個位元組資料
void SPI_send(uint8_t data) {
SPDR = data; // 將資料寫入 SPI 暫存器
while (!(SPSR & (1 << SPIF))); // 等待傳輸完成
}
// 顯示一個數字在指定的 7段顯示器上
void display_digit(int digit) {
SPI_send(digit_codes[digit]); // 傳送數字資料
PORTB |= (1 << SS); // 鎖存輸出
PORTB &= ~(1 << SS); // 解除鎖存
PORTB &= ~(1 << PB0);
}
// 外部中斷 0 (PD2):切換顯示狀態
ISR(INT0_vect) {
paused = !paused; // 循環切換
}
// 外部中斷 1 (PD3):增加閃爍中的數位數值
ISR(INT1_vect) {
if (paused) { // 僅在暫停狀態時有效
num = (num < 99) ? num + 1 : 0;
}
}
void setup() {
SPI_init(); // 初始化 SPI
DDRB |= (1 << PB0) ; // 設置 PB0, PB1 為顯示器選擇輸出
// 設定 PD2 (INT0) 和 PD3 (INT1) 為輸入,並啟用內部上拉電阻
DDRD &= ~((1 << PD2) | (1 << PD3));
PORTD |= (1 << PD2) | (1 << PD3);
// 設定外部中斷在「下降沿」觸發
EICRA |= (1 << ISC01) | (1 << ISC11);
EIMSK |= (1 << INT0) | (1 << INT1); // 啟用 INT0 和 INT1 中斷
sei(); // 啟用全域中斷
Serial.begin(115200);
}
void loop() {
if (!paused) { // 正常計數模式
if (count >= flash_persec) {
num = (num < 9) ? num + 1 : 0; // 計數至 99 後歸零
count = 0;
}
display_digit(num);
_delay_ms(flash_time * 2);
count++;
}
else if (blink <= (flash_persec / 2)) {
PORTB |= (1 << PB0); // 關閉7段
_delay_ms(flash_time * 2);
blink = (blink + 1) % 20;
}
else {
display_digit(num);
_delay_ms(flash_time * 2);
blink = (blink + 1) % 20;
}
Serial.println(num);
}