#c #embedded #microcontroller #avr #watchdog
Вопрос:
Привет, я использую сторожевой пес для управления светодиодными лампами. микроконтроллер подключен к ноутбуку с помощью кабеля. Вход на микроконтроллер составляет 5 В. теперь на выводах есть один вывод PB2
, который 5V
напрямую подключен к входу. Я хочу сделать это, если я выну провод 5 В от женщины к мужчине из PB2
светодиодного выключателя. когда я снова подключаюсь к PB2
нему, после этого включается свет, вызывается сторожевой пес и выключает красный свет каждые 4 секунды
#include <avr/io.h>
#include <avr/interrupt.h>
#include <avr/wdt.h>
#define WDTO_4S 8
void fun_red(){
PORTB.IN=0x04; // PB2 as input
PORTB.DIR=0x09; // PB0 and PB3 output
PORTB.OUTCLR=0x00; // Green
PORTB.OUTSET=0x01;
wdt_enable(WDTO_4S);
do
{
if (PORTB.IN)
{
PORTB.OUTCLR=0x00; // red
}
else{
PORTB.OUTSET=0x08;
}
} while(1);
wdt_reset();
}
int main(void)
{
fun_red();
}
Комментарии:
1. Ты никогда этого не сделаешь
wdt_reset()
. Кроме того, почему вы определяете#define WDTO_4S 8
? Вы должны использовать макросы из<avr/wdt.h>
.2. Кроме того, мне непонятно, зачем вы вообще используете сторожевого пса.
3. @ThomasJager я хочу посмотреть, как светодиод в течение 4 секунд включит красный свет, а затем понизит напряжение и выключит его
4. @ThomasJager Я использую AVR128DB48 Curiosity Nano
5. Все это не имеет никакого смысла, и я даже не знаю, что вы пытаетесь сделать. Если вы пытаетесь использовать сторожевой пес для реализации функциональности приложения, то это очень плохая идея, о которой вам следует забыть.
Ответ №1:
Хорошо, итак, в правке кажется, что вы хотите использовать PB2 в качестве входа для управления светодиодом на PB3 — это правильно?
Вы не должны думать в терминах напряжений, это цифровой ввод/вывод, у них есть высокое состояние и низкое состояние и порог между ними. Входы с вашей стороны будут повышаться при напряжении выше 0,8 Вольт. Однако снятие входного напряжения путем отключения провода не будет иметь никакого эффекта, если вы включили внутреннее подтягивание, а если нет, оно будет плавающим и может не вызвать логический 0 (ноль).
Вам либо нужен внешний понижающий резистор, либо вы включаете внутреннее подтягивание и инвертируете логику-т. е. индикатор включается, когда вход низкий, и выключается, когда вход высокий. Затем вместо подключения PB2 к 5 В вы подключаете его к GND. Удаление подключения GND приведет к тому, что логическое состояние станет 1 из-за внутренней подтяжки. Кроме того, это намного безопаснее, поскольку позволяет избежать любого риска приложения чрезмерного напряжения на вход и повреждения процессора.
Учитывая ваше очевидное незнание электроники, я бы также спросил, как вы подключили светодиод. У вас должен быть последовательный резистор, ограничивающий ток, и светодиод должен быть подключен правильно.
Остается неясным, какова цель сторожевого пса в этом. Это не служит никакой цели, но если вы включите его, вы должны поддерживать его, чтобы предотвратить сброс процессора. Цель сторожевого пса состоит в том, чтобы сбросить процессор, если программное обеспечение перестает нормально работать, регулярно сбрасывая его по обычному пути выполнения программного обеспечения, поэтому, если обычный путь прекращает выполнение, система перезапускается.
Start by reading this primer App Note: Getting Started with GPIO
First of all, you have misunderstood the function of the GPIO registers. PORTB.IN
reads the input states of the PORTB pins that are configured as input. It is read-only so:
PORTB.IN=0x04; // PB2 as input
has no effect and certainly does not configure the pin as an input. That is done by DDR
, so:
PORTB.DIR=0x09; // PB0 and PB3 output
sets PB0 and PB3 as outputs, and anything that is not an output is an input — so PB2 is an input by virtue of this line.
Now if you do as I recommend and use the internal pull-up for PB2, you must enable that in the PIN2CTRL
register:
PORTB.PIN2CTRL = 0x08 ; // PULLUPEN
Incidentally there are symbols defined for all thse bits so you should be able to write:
PORTB.PIN2CTRL = PORT_PULLUPEN_bm ;
что также делает комментарий ненужным.
Регистры OUTSET
and OUTCLR
, set (логика 1/высокий) и clear (логика 0/низкий) соответственно. Контакты, которые необходимо установить/очистить, определяются переданной битовой маской. Как таковой:
PORTB.OUTCLR=0x00; // Green
ничего не делает, он не переводит контакты в низкое состояние. Чтобы установить красный светодиод (я предполагаю, что PB3) выключен, а зеленый светодиод (PB0) включен:
PORTB.OUTCLR = PIN3_bm ;
PORTB.OUTSET = PIN0_bm ;
Теперь ваш тест PORTB.IN
, как если бы он был логическим, будет работать в этом случае, потому что у вас есть только один вход. Но если у вас несколько входных данных, он не будет различать их, и в любом случае это плохая привычка использовать целочисленное выражение, как если бы оно было логическим. Вы должны явно проверить состояние PB2:
// If PB2 is low (GND wire connected)
if( (PORTB.IN amp; PIN2_bm) == 0 )
{
PORTB.OUTSET = PIN3_bm ; // red on
}
else
{
PORTB.OUTCLR = PIN3_bm ; // red off
}
Назначение сторожевого таймера-перезагрузить процессор, если программное обеспечение не работает нормально. Вы устанавливаете время ожидания, затем вам нужно регулярно сбрасывать его в коде, чтобы предотвратить сброс. Это не общая цель, которую вы использовали бы для задержки. Для этого вы бы использовали аппаратный таймер. К сожалению, это становится немного сложным; для вашей платы, работающей на частоте 24 МГц, максимальный период таймера для 16-битного таймера 1 составляет около 2,8 секунды. Для большей гибкости вы обычно реализуете прерывание таймера для подсчета количества меньших периодов и подсчета количества перезагрузок таймера. Например:
volatile unsigned tick = 0 ;
ISR (TIMER1_OVF_vect) // Timer1 ISR
{
tick ;
}
void tickStart()
{
TCCR1B = (1<<CS11) // Prescaler 24MHz / 8
TCNT1 = 3000 ; // 1 ms at 24MHz/8
TCCR1A = 0x00;
TIMSK = (1 << TOIE1) ; // Enable timer1 overflow interrupt(TOIE1)
sei(); // Enable global interrupts
}
unsigned getTick()
{
unsigned t = 0 ;
do
{
t = tick ;
} while( tick != t ) ;
return t ;
}
Затем в течение 4-секундной задержки, одновременно контролируя P2, вы можете сделать:
// Wait 4 seconds or until PB2 disconnected
unsigned start = getTick() ;
while( getTick() - start < 4000 amp;amp;
(PORTB.IN amp; PIN2_bm) != 0 )
{
// waiting
}
Наконец, прокомментируйте свой код. Это позволит вам понять, что вы пытаетесь сделать, и когда вы зададите вопрос, он расскажет другим, что вы пытаетесь сделать, и это упростит ваш вопрос, если объяснение будет соответствовать коду, чтобы было ясно не только то, что вы хотите, чтобы код делал, но и как вы думаете, что он это делает.
Если сложить все это вместе, то более правдоподобным выглядит следующее:
void fun_red( void )
{
// Initialise I/O
PORTB.PIN2CTRL = PORT_PULLUPEN_bm ;
PORTB.DIR = PIN0_bm | PIN3_bm ;
PORTB.OUTSET = PIN0_bm ; // Green on
for(;;)
{
// Red off
PORTB.OUTCLR = PIN3_bm ;
// Wait for PB2 to be connected (to GND)
while( (PORTB.IN amp; PIN2_bm) == 0 )
{
// waiting
}
// Red on
PORTB.OUTSET = PIN3_bm ;
// Wait 4 seconds or until PB2 disconnected
unsigned start = getTick() ;
while( getTick() - start < 4000 amp;amp;
(PORTB.IN amp; PIN2_bm) != 0 )
{
// waiting
}
// Wait for PB2 to be reconnected
while( (PORTB.IN amp; PIN2_bm) != 0)
{
// waiting
}
}
}
int main( void )
{
tickStart() ;
fun_red() ;
}
Remember in this you connect PB2 to GND (0 volts) not not 5V/Vcc. The requirements in the question are not entirely clear but what this will do (untested — I don’t have the hardware) is:
- while PB2 is disconnected, the LED will be off,
- когда PB2 подключен, светодиод будет гореть в течение 4 секунд или до тех пор, пока PB2 не будет снова подключен.
Хотя это не является его целевым назначением и на самом деле не полезно для общего назначения, поскольку это требование очень простое, мы можем использовать сторожевой таймер для реализации желаемого поведения следующим образом:
void fun_red()
{
// Initialise I/O
PORTB.PIN2CTRL = PORT_PULLUPEN_bm ;
PORTB.DIR = PIN0_bm | PIN3_bm ;
// Green on
PORTB.OUTSET = PIN0_bm ;
// Red off
PORTB.OUTCLR = PIN3_bm ;
// Enable watchdog
wdt_enable(WDTO_4S);
for(;;)
{
// Maintain watchdog while waiting for
// PB2 to be connected (to GND)
while( (PORTB.IN amp; PIN2_bm) == 0 )
{
wdt_reset() ;
}
// Red on
PORTB.OUTSET = PIN3_bm ;
// Maintain watchdog while waiting for
// PB2 to be disconnected (from GND)
while( (PORTB.IN amp; PIN2_bm) != 0)
{
wdt_reset() ;
}
// Red off
PORTB.OUTCLR = PIN3_bm ;
// Wait for PB2 to be reconnected (to GND)
// without maintaining watchdog. Will reset after 4 seconds
// if not reconnected.
while( (PORTB.IN amp; PIN2_bm) != 0)
{
// do nothing
}
}
}
Здесь после отключения PB2 сторожевой таймер не поддерживается, так что через 4 секунды произойдет сброс, и он перезапустит программу и дождется подключения PB2. Я думаю, что я должен классифицировать такой код как «грязный трюк», а не то, что можно считать нормальным или особенно полезным.
Комментарии:
1. Оператору нужно прочитать это, и, возможно, вы тоже это сделали, Клиффорд, это готовая доска. PB2, как задокументировано, подключен между PB2 и землей через переключатель (обычно можно предположить, что мгновенный SPST открыт). И в нем говорится, что вам не нужно подтягиваться, чтобы включить подтягивание в mcu. Это означает, что PB2 обычно высокий (точное значение напряжения не имеет значения, считывается как 1 в gpio), а при нажатии считывается как низкий. Светодиод связан между vcc_target через резистор с pb3, что означает, что запись 0 заставляет светодиод включаться, а 1-гаснуть.
2. как написано в вопросе и ответе, он будет просто отражать кнопку. Если, как в конце вопроса, вы хотите, чтобы индикатор включался в течение 4 секунд при нажатии кнопки, вам нужно выполнить больше работы, и вы не хотите использовать WDT, используйте какой-либо другой таймер. или, если вы используете wdt, затем, если кнопка нажата (если pb2 низкий), затем включите светодиод и перейдите в бесконечный цикл, который не вызывает сброс wdt, И установите wdt на 4 секунды…
3. @old_timer На какую документацию вы ссылаетесь? Я ссылался на данные Curiosity Nano , и бортовой переключатель находится на PC7, а светодиод на PC6. Однако возникает вопрос, почему этот код не будет использовать как встроенный переключатель, так и встроенный светодиод-по крайней мере, чтобы избежать необходимости разбираться в электронике.
4. Я уже заявил о необходимости использовать внутреннее подтягивание-я не понимаю вашей точки зрения — ваш комментарий повторяет большую часть моего ответа (возможно, вы могли бы прочитать это ; -). Если у вас есть ответ, опубликуйте его, если есть проблема с этим ответом, будьте конкретны — я исправлю это (было поздно).
5. Я действительно пропустил момент включения на 4 секунды — хотя это тоже было разъяснено (вроде как) в правке. Код операции, по-видимому, не указывает на это, и таймер сторожевого пса явно не имеет значения. Исправлю.