目前我有 Atmega8 1MHz 微控制器。我正在使用 C 語言。所以基本上我的程式在 7 段顯示幕上顯示數字。每次按下按鈕它都會增加數字 1
現在我想測量時間,如果用戶按下按鈕 3 秒。我唯一需要的是如何測量中斷是否達到 3 秒。我需要它背后的邏輯,我應該延遲使用 if 陳述句嗎?或者我不知道也許還有別的
這是我的代碼:
#define F_CPU 1000000UL
#define IRQ1 INT0_vect
#define IRQ2 INT1_vect
#include <avr/io.h>
#include <util/delay.h>
#include <avr/interrupt.h>
volatile int the_count = 0;
volatile int i;
volatile int k;
ISR(IRQ1){
if (the_count < 100){
the_count ;
i = the_count % 10;
k = the_count / 10;
}
else{
the_count = 0;
i = the_count % 10;
k = the_count / 10;
}
}
ISR(IRQ2){
if (the_count < 100){
the_count = the_count;
i = the_count % 10;
k = the_count / 10;
}
else{
the_count = 0;
i = the_count % 10;
k = the_count / 10;
}
}
void init()
{
DDRD = 0b00000111;
DDRB = 0b11111111;
PORTB = 255;
_delay_ms(2000);
PORTB = 0;
GICR = 0xc0;
MCUCR = 0x08;
IRQ1;
IRQ2;
}
int main(void){
init();
GICR = 0xc0;
MCUCR = 0x08;
sei();
int digit[] = {0b000000101, 0b10111101, 0b00100110, 0b10100100, 0b10011100, 0b11000100, 0b01000100, 0b10101101, 0b00000100, 0b10001100};
int dig1 = 0b00000110; //first digit area on display
int dig2 = 0b00000101; //second digit area on display
while (1)
{
if (k < 1){
PORTB = digit[0];
PORTD = dig1;
_delay_ms(1);
PORTD = dig2;
PORTB = digit[i];
_delay_ms(1);
}
else if (k>=1 && i==0 && the_count<100){
PORTB = digit[k];
PORTD = dig1;
_delay_ms(1);
PORTD = dig2;
PORTB = digit[0];
_delay_ms(1);
}
else if (k>=1 && i!=0 && the_count<100){
PORTB = digit[k];
PORTD = dig1;
_delay_ms(1);
PORTD = dig2;
PORTB = digit[i];
_delay_ms(1);
}
else {
the_count = 0;
}
}
}
uj5u.com熱心網友回復:
我不打算通過所有代碼來回答您的問題——尤其是因為您的命名約定、缺少注釋或 I/O 抽象使得很難確定什么 I/O 是什么或什么代碼做什么。但是“邏輯”如下(將時序和 I/O 呼叫替換為您可用的或由您酌情實作的呼叫):
bool buttonPressHoldDetect( uint32_t hold_time_millisec )
{
static bool release_pending = false ;
bool button_hold_time_event = false ;
static bool previous_button_pressed_state = isButtonPressed() ;
bool button_pressed = isButtonPressed() ;
// If button down...
if( button_pressed )
{
// If it was previously up...
if( !previous_button_pressed_state )
{
// Timestamp the button down event
uint32_t button_down_start = getTickMillisec() ;
}
// If button held down for hold time...
if( !release_pending &&
(getTickMillisec() - button_down_start) > hold_time_millisec )
{
button_hold_time_event = true ;
release_pending = false ;
}
}
else
{
// button released, allow a subsequent press to be timed
release_pending = false ;
}
// Retain previous state for event detection
previous_button_pressed_state = button_pressed ;
// Return true when the button has been
// held for hold time
return button_hold_time_event;
}
然后你會有一個形式的輪詢回圈:
for(;;)
{
// If button held for three seconds...
if( buttonPressHoldDetect( 3000 ) ;
{
// Do button held for three second stuff
}
// Do other stuff
}
零延遲/無忙等待輪詢允許您在回圈中執行其他作業。當然,其他作業也應該避免延遲和忙等待 - 您的回圈時間(以及因此按鈕輪詢率)是回圈中完成的所有作業(包括延遲)的總和,因此對于回應速度最快的系統,您需要盡量減少完成的作業并使回圈盡可能具有確定性(恒定的回圈時間)。
具體來說:
您不需要使用中斷(也許是計時中斷)。你可以,并且中斷可能只是設定一個
isButtonPressed()
例如回傳的標志,但它只會增加復雜性(特別是 wrt switch de-bounce),我建議簡單地輪詢它,你不應該使用延遲——在延遲期間你不能做其他有用的作業。(除非您使用的是多執行緒調度程式)。改用時間戳和經過時間,并避免任何形式的“忙等待”。
您確實需要考慮開關彈跳,并且可以類似地處理:
bool isButtonPressed()
{
static const uint32_t DEBOUNCE_MILLISEC = 20 ;
static bool button_pressed = readInput( BUTTON ) != 0 ;
static uint32_t button_event_time = 0 ;
// Get the current time and button state
uint32_t now = getTickMillisec() ;
bool current_button_state = readInput( BUTTON ) != 0 ;
// If button changes state after the debounce period...
if( (now - button_event_time) > DEBOUNCE_MILLISEC
&& current_button_state != button_pressed )
{
// Change the button state and timestamp the event
button_pressed = current_button_state ;
button_event_time = now ;
}
return button_pressed ;
}
此外,如果在回圈中完成的作業很少,但您想要一個比主體執行時間更長的特定且確定性的回圈時間(例如在 PID 控制回圈中這是必不可少的),那么:
static const int LOOP_TIME_MILLISEC = 20 ; // loop 50 times per second
int loop_start_time = 0 ;
for(;;)
{
uint32_t now = getTickMillisec() ;
// if time to start loop iteration...
if( (now - loop_start_time) >= LOOP_TIME_MILLISEC )
{
loop_start_time = now ;
// If button held for three seconds...
if( buttonPressHoldDetect( 3000 ) ;
{
// Do button held for three second stuff
}
// Do other stuff
}
}
我很長時間沒有使用 AVR,但如果您還沒有合適的計時功能getTickMillisec()
(連同初始化和 ISR),可以通過以下方式實作:
#include <avr/io.h> ;
#include <avr/interrupt.h> ;
// Timer reload value for 1ms
#define SYSTICK_RELOAD (1000000UL / 1000)
// Millisecond counter
volatile uint32_t tick_millisec = 0 ;
ISR (TIMER1_COMPA_vect)
{
tick_millisec ;
}
void sysTickInit()
{
// CTC mode, Clock/1
TCCR1B |= (1 << WGM12) | (1 << CS10);
// Load the output compare
OCR1AH = (SYSTICK_RELOAD >> 8);
OCR1AL = SYSTICK_RELOAD ;
// Enable the compare match interrupt
TIMSK1 |= (1 << OCIE1A);
// Enable interrupts
sei();
}
uint32_t getTickMillisec()
{
uint32_t now = 0 ;
// Read tick count and re-read if it is not consistent
// (due interrupt pre-emption and update during non-atomic access)
do
{
now = tick_millisec ;
} while( now != tick_millisec ) ;
return now ;
}
請注意回圈輸入getTickMillisec()
- 在 8 位設備上訪問tick_millisec
是非原子的,因此 ISR 可能會在讀取它的程序中進行部分更新。回圈重新讀取它,直到它是一致的(即相同的值讀取兩次)。或者,您可以簡單地禁用中斷:
uint32_t getTickMillisec()
{
// Read tick count with interrupts disabled to ensure consistency
cli() ;
uint32_t now = tick_millisec ;
sei() ;
return now ;
}
但這通常會影響中斷處理程式的時間,因此最好避免。
有關 16 位 TIMER1 的詳細資訊,請參見第 75 頁的ATmega8 資料表。
轉載請註明出處,本文鏈接:https://www.uj5u.com/gongcheng/470625.html