В настоящее время я могу читать байт за байтом из USART с помощью этого кода
ISR(USART_RX_vect)
{
cli();
while(!(UCSR0A&(1<<RXC0))){};
// clear the USART interrupt
received = UDR0;
if(pinState == 0)
{
OCR2A = received;
pinState = 1;
}
else if(pinState == 1)
{
OCR2B = received;
pinState = 2;
}
else
{
OCR0A = received;
pinState = 0;
}
sei();
}
Но теперь я отправлю 4 байта данных, которые необходимы только для моего приложения. Я не мог понять, как читать 4 байта сразу, так как прерывание срабатывает для каждого байта. Спасибо за любые усилия заранее.
Чтобы было понятнее, я кратко объясню, что это pinState
такое. Я отправляю 3 байта данных, и я хочу, чтобы 1-й байт передавался 2-му pin 3 pwm
байту pin 11 pwm
и 3-му байту в pin6 pwm
. Как вы видите, в каждом прерывании я получаю не 3 байта, а 1 байт. Так что эта вещь pinState предназначена только для этой цели.
Вы не упомянули, какой микроконтроллер вы используете, но это, вероятно, не имеет значения. Периферийные устройства USART обычно работают именно так, как вы обнаружили: по одному байту за раз. Однако это не является ограничением.
Основываясь на фрагменте кода, который вы разместили в своем вопросе, вы пытаетесь выполнить некоторые функции с каждым полученным байтом. Это ограничивает вас однобайтовыми операциями. Что, если вместо этого вы использовали ISR только для заполнения массива байтов? Затем, после поступления определенного количества байтов, только тогда вы считываете байты и интерпретируете их значение — желательно в основном цикле вашего кода, а не в ISR.
Будьте осторожны, не выполняйте слишком много работы в ISR. Особенно вызов функций из функции ISR. Конечно, все производители микросхем и компиляторы разные, но вызов функций в ISR часто может привести к чрезвычайно медленному и неэффективному коду. Большинство ваших вычислений и обработки данных должны происходить в вашем основном цикле. ISR следует использовать для установки флагов и быстрых операций. Как правило, вы хотите выйти из ISR как можно быстрее.
Обычный метод обработки многобайтовых приемов в подпрограмме прерывания заключается в настройке циклической очереди. По мере получения каждого байта он помещается в следующий доступный слот в очереди. Это заменит ваш однобайтовый код сохранения, который у вас есть сейчас.
Выходная сторона циклической очереди опрашивается на наличие доступного контента вашим основным кодом, который хочет использовать полученные данные. Когда вы начинаете работать с многобайтовыми потоками на последовательном интерфейсе, часто желательно создать «протокол», который позволит принимающей стороне отслеживать, с чего начинается каждый пакет данных. Есть много способов сделать это. Один из способов — выбрать уникальное значение байта для начала пакета. Другой способ — установить MSB первого байта пакета, а затем установить все остальные байты пакета с очищенным MSB.
** Некоторые комментарии о вашем ISR **
Как правило, размещение циклов опроса внутри процедуры обслуживания прерываний является плохой практикой. Вы хотите, чтобы время выполнения ISR было как можно короче. Я не знаю, какова цель цикла опроса, но вам следует изучить возможность его удаления.
Обычно нет необходимости отключать и снова включать прерывания внутри ISR. Большинство MCU автоматически отключают прерывания при входе в ISR, а затем восстанавливают предыдущее состояние при выполнении возврата из прерывания.
Непонятно, что делает логика состояния вывода внутри ISR. Это совершенно не применимо к обслуживанию прерывания USART.
Как раз сегодня я оказался в точно таком же положении и написал программу в соответствии с тем, что предлагает Майкл Карас в своем ответе, используя круговой буфер. Я использовал PIC18, поэтому некоторый код может не скомпилироваться, но он ясно показывает идею, и этот код должно быть легко портировать на AVR, ...
Я объявил некоторые глобальные переменные:
#define EUSART_BUFFER_SIZE 2048
char eusart_rx_buffer[EUSART_BUFFER_SIZE]; // the actual buffer, now 2048 bytes long
uint16_t eusart_rx_buffer_rd = 0; // the current read position
uint16_t eusart_rx_buffer_wr = 0; // the current write position
Предполагается stdint.h
, что он включен для uint16_t
типа.
Идея такова:
eusart_rx_buffer[eusart_rx_buffer_wr]
и увеличиваем позицию записи.eusart_rx_buffer_rd
до eusart_rx_buffer_wr
.Конечно, когда одновременно сохраняется более 2048 байт, буфер будет перезаписан, и вы потеряете данные. Однако есть некоторые приемы, которые вы можете использовать, чтобы обойти это. Вы можете изменить EUSART_BUFFER_SIZE
в соответствии с вашими потребностями. Меньшее значение, конечно, требует меньше памяти для данных.
Теперь в моем ISR у меня есть:
if (PIR1bits.RCIF) { // EUSART data received
eusart_rx_buffer[eusart_rx_buffer_wr++] = RCREG; // Store the received data
if (eusart_rx_buffer_wr >= EUSART_BUFFER_SIZE) // Increment write pointer
eusart_rx_buffer_wr = 0;
PIR1bits.RCIF = 0; // Clear interrupt flag
}
Конечно, на AVR этот код будет выглядеть немного иначе, но идея та же.
Затем, где вы хотите прочитать данные, вы можете сделать что-то вроде:
while (eusart_rx_buffer_rd != eusart_rx_buffer_wr) { // While there's data in the buffer
do_sth(eusart_rx_buffer[eusart_rx_buffer_rd++]); // Do something with it
if (eusart_rx_buffer_rd >= EUSART_BUFFER_SIZE) // Increase read pointer
eusart_rx_buffer_rd = 0;
}
Этот код был написан для PIC18 с использованием компилятора XC8, но большая часть его написана на стандартном C и может быть легко скопирована или перенесена.
Голаж
cli();
ваш ISR, поскольку он автоматически выполняется MCU.Згркпнр__
РБертейг
хобби