按下按钮 3 秒以及如何使用 Atmega8 1MHz 测量其时间?
Pressing button for 3 seconds and how to measure its time with Atmega8 1MHz?
目前我有 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;
}
}
}
我不会费力地浏览所有代码来回答您的问题 - 尤其是因为您的命名约定、缺少注释或 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
}
zero-delay / no busy-wait 轮询允许您在循环中执行其他工作。当然,其他工作也应该避免延迟和 busy-waits - 你的 loop-time(因此按钮轮询率)是循环中完成的所有工作(包括延迟)的总和,所以对于大多数响应式系统,您需要尽量减少完成的工作并使循环尽可能具有确定性(恒定循环时间)。
具体来说:
您不需要使用中断(除了用于定时的滴答中断之外)。你可以,并且中断可能只是设置一个由 isButtonPressed()
返回的标志,但它只会增加复杂性(尤其是 w.r.t.switch de-bounce),我建议简单地轮询它,
你不应该使用延迟 - 在延迟期间你不能做其他有用的工作。
(除非您使用 multi-thread 调度程序)。请改用时间戳和经过时间,并避免使用任何类型的“busy-waits”。
您确实需要考虑开关弹跳,这可以类似地处理:
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
是 non-atomic,因此 ISR 可能会在读取它的过程中更新。循环 re-reads 直到它一致(即相同的值读取两次)。或者你可以简单地禁用中断:
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 datasheet。
目前我有 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;
}
}
}
我不会费力地浏览所有代码来回答您的问题 - 尤其是因为您的命名约定、缺少注释或 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
}
zero-delay / no busy-wait 轮询允许您在循环中执行其他工作。当然,其他工作也应该避免延迟和 busy-waits - 你的 loop-time(因此按钮轮询率)是循环中完成的所有工作(包括延迟)的总和,所以对于大多数响应式系统,您需要尽量减少完成的工作并使循环尽可能具有确定性(恒定循环时间)。
具体来说:
您不需要使用中断(除了用于定时的滴答中断之外)。你可以,并且中断可能只是设置一个由
isButtonPressed()
返回的标志,但它只会增加复杂性(尤其是 w.r.t.switch de-bounce),我建议简单地轮询它,你不应该使用延迟 - 在延迟期间你不能做其他有用的工作。 (除非您使用 multi-thread 调度程序)。请改用时间戳和经过时间,并避免使用任何类型的“busy-waits”。
您确实需要考虑开关弹跳,这可以类似地处理:
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
是 non-atomic,因此 ISR 可能会在读取它的过程中更新。循环 re-reads 直到它一致(即相同的值读取两次)。或者你可以简单地禁用中断:
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 datasheet。