AVR Раздел по микроконтроллерам компании Atmel - AVR / ATtiny / ATmega / ATMega128 / ATxmega, вопросы по программированию в AVR studio и все, относящееся к AVR... |
04.09.2013, 20:00
|
#51
|
Почётный гражданин KAZUS.RU
Регистрация: 29.10.2006
Сообщений: 1,443
Сказал спасибо: 99
Сказали Спасибо 315 раз(а) в 231 сообщении(ях)
|
Re: Глюк с UART при засыпании
Сообщение от Someone
|
И чем не устраивает равенство нулю счётчика байт?
Код:
|
ISR(USART1_TX_vect) // прерывание завершения передачи символа
{
ByteCountUART--; // уменьшаем счётчик переданных байт
if(!ByteCountUART) // если передали все байты
{
TransiverOff(); // отключаем передатчик RS485
return; // завершаем прерывание
}
UDR1 = *PosBuferUART1++; // если послали не всё, посылаем очередной байт
} |
Очевидно же, что если ByteCountUART равен 0, передача была полностью завершена, и можно спать или заниматься чем угодно.
|
А вот и нет.
ByteCounter = 1
То есть, в буфере есть 1 байт, требующий отправки. Зашли в прерывание. ByteCounter стал 0. И пошла отправка байта. На скорости 115200 она будет идти 87 мкс. Мы вышли из прерывания и вошли в функцию Flush(). Там мы узнали, что ByteCounter равен нулю, и соответственно, вышли из неё. И всё. Засыпаем. А в это время ещё идёт передача последнего байта.
Сообщение от av0000
|
Someone, в итоге примерно так и вышло.
Оговорка: декремент счётчика без проверки может вызвать переполнение, потому, уменьшаем только если не ноль
|
Но после уменьшения делать ещё одну проверку (как у вас) не надо.
|
|
|
|
05.09.2013, 00:03
|
#52
|
Гражданин KAZUS.RU
Регистрация: 16.06.2005
Сообщений: 944
Сказал спасибо: 25
Сказали Спасибо 174 раз(а) в 123 сообщении(ях)
|
Re: Глюк с UART при засыпании
Сообщение от av0000
|
не требуется дублировать код отправки первого байта в uart_putch().
|
Гм... не понял, о чём это? Зачем что-то дублировать? Первый байт пакета из буфера посылается обычным образом:
Код:
|
PosBuferUART1 = &buferUART1[0]; // задаём адрес начала кадра
ByteCountUART = 5; // длина кадра
TransiverOn(); // включаем передатчик RS485
UDR1 = *PosBuferUART1++; // посылаем 1й байт ответа |
И всё, дальше весь пакет передаётся автоматически, и не требует никакого вмешательства в прцесс передачи. Как только закончится передача 1го байта, сработает прерывание, которое уменьшит счётчик и если он не 0, пхнёт в удр очередной байт. ByteCountUART при входе в прерывание по определению не может быть равным 0, т.к. длину пакета задаём (если только не забыть задать длину пакета перед записью в удр первого байта пакета, что сразу будет заметно), потому-то проверка перед декрементом счётчика не нужна. Зато равенство этого счётчика 0 однозначно укажет что передача пакета завершилась.
|
|
|
|
05.09.2013, 00:32
|
#53
|
Гражданин KAZUS.RU
Регистрация: 16.06.2005
Сообщений: 944
Сказал спасибо: 25
Сказали Спасибо 174 раз(а) в 123 сообщении(ях)
|
Re: Глюк с UART при засыпании
Сообщение от Godzilla82
|
А вот и нет.
|
А вот и да.
Сообщение от Godzilla82
|
ByteCounter = 1
|
Т.е. надо передать 1 байт, ну скажем 0х55 например
ByteCountUART = 1; // длина кадра
UDR1 = 0х55;
Байт попадёт в удр, перепишется в сдвиговый регистр, установится флаг удре (что правильно, т.к. байт ушел из буфера, и буфер пуст, но нам на этот флаг плевать, нет у нас такого прерывания, не используем мы его), начнётся передача байта в линию связи, и когда он передастся, установится флаг тхс, и мы попадаем в прерывание, где счётчик, равный пока 1, декрементируется (мы ведь и в самом деле закончили передачу 1го байта), становится равным 0, что означает что ничего более передавать не надо, и прерывание завершается, не записав в удр ничего более. И соответственно более не возникнет, пока пользователь не запишет в удр новый байт, и пока не завершится передача этого байта в линию.
Сообщение от Godzilla82
|
Мы вышли из прерывания и вошли в функцию Flush().
|
Какую флуш? Зачем она нам? Если есть желание ждать конца передачи, то
while(ByteCountUART);
sleep();
или если охота 87 мкс заниматься чем-либо второстепенным, через
if(!ByteCountUART) sleep();
и всё. И стек лишний раз не дёргается (вызов, сохранение регистров, восстановление регистров, возврат), и код меньше (call, ret).
Сообщение от Godzilla82
|
А в это время ещё идёт передача последнего байта.
|
Это с чего бы? Прерывание TXC возникает после завершения передачи байта. В том-то вся прелесть и заключается, что войдя в этот обработчик прерывания мы знаем, что символ уже передался.
Последний раз редактировалось Someone; 05.09.2013 в 00:46.
|
|
|
|
05.09.2013, 01:50
|
#54
|
Почётный гражданин KAZUS.RU
Регистрация: 29.10.2006
Сообщений: 1,443
Сказал спасибо: 99
Сказали Спасибо 315 раз(а) в 231 сообщении(ях)
|
Re: Глюк с UART при засыпании
Ну да, в голове, что ByteCounter одновременно является индексом элемента массива, из которого идёт передача... А так да, всё верно.
|
|
|
|
05.09.2013, 09:40
|
#55
|
Частый гость
Регистрация: 27.08.2008
Адрес: Москва
Сообщений: 29
Сказал спасибо: 6
Сказали Спасибо 1 раз в 1 сообщении
|
Re: Глюк с UART при засыпании
Сообщение от Someone
|
И всё, дальше весь пакет передаётся автоматически,
|
Про первый байт и шла речь. Таки да, там не весь код получился, а только лишняя проверка (приведённый пример её не учитывает) - в UDR пишем только _первый_ байт, т.е. если буфер пуст. Но это не сравнимая "цена" с кучей push/pop в дополнительном обработчике прерываний
PHP код:
|
putch(c) {
///
if (_tx.cnt) {
_tx.buf[_tx.in] = data;
_tx.in = (_tx.in + 1) & UART_TX_BUF_MASK;
} else {
UDR = data;
}
_tx.cnt++;
///
}
|
PHP код:
|
ISR(USART0_TXC_vect) {
_tx.cnt--;
if (_tx.cnt) {
UDR = _tx.buf[_tx.out];
_tx.out = (_tx.out + 1) & UART_TX_BUF_MASK;
} else {
#ifdef RS485PIN
RS485PORT &= ~(1‹‹RS485PIN);
#endif
;
}
}
|
ну и flush в таком варианте, действительно, вырождается в `while(_tx.cnt) ;`
( полный код всего этого безобразия )
|
|
|
|
05.09.2013, 20:27
|
#56
|
Почётный гражданин KAZUS.RU
Регистрация: 29.10.2006
Сообщений: 1,443
Сказал спасибо: 99
Сказали Спасибо 315 раз(а) в 231 сообщении(ях)
|
Re: Глюк с UART при засыпании
Подолью ещё маслица в огонь.
Буфер устроен коряво. Проще было бы кольцевой буфер. И к тому же произвольного размера.
|
|
|
|
05.09.2013, 20:33
|
#57
|
Гражданин KAZUS.RU
Регистрация: 16.03.2011
Сообщений: 486
Сказал спасибо: 8
Сказали Спасибо 131 раз(а) в 116 сообщении(ях)
|
Re: Глюк с UART при засыпании
Сообщение от Godzilla82
|
Буфер устроен коряво. Проще было бы кольцевой буфер.
|
Он и так кольцевой.
Сообщение от Godzilla82
|
И к тому же произвольного размера.
|
Произвольного - это как?
|
|
|
|
05.09.2013, 21:06
|
#58
|
Почётный гражданин KAZUS.RU
Регистрация: 29.10.2006
Сообщений: 1,443
Сказал спасибо: 99
Сказали Спасибо 315 раз(а) в 231 сообщении(ях)
|
Re: Глюк с UART при засыпании
Сообщение от _Артём_
|
Он и так кольцевой.
|
Хотя да, он кольцевой. Всё думаю о том, что в прикреплённом файле из первых постов.
А тут уже всё поменялось.
Сообщение от _Артём_
|
Произвольного - это как?
|
Не кратного степеням двойки. Хотя это и не принципиально.
Что-то типа этого
PHP код:
|
#define TX_BUFFER_SIZE 50
unsigned char tx[TX_BUFFER_SIZE];
unsigned char tx_read = 0; unsigned char tx_write = 0;
ISR(USART0_TXC_vect) { tx_read++; if(tx_read ›= TX_BUFFER_SIZE) tx_read = 0; if(tx_read != tx_write) UDR = tx[tx_read]; }
void usart_putchar(unsigned char c); { cli(); tx[tx_write] = c; if(tx_write == tx_read) UDR = c; tx_write++; if(tx_write ›= TX_BUFFER_SIZE) tx_write = 0; sei(); }
|
Последний раз редактировалось Godzilla82; 05.09.2013 в 21:09.
|
|
|
|
06.09.2013, 09:33
|
#59
|
Гражданин KAZUS.RU
Регистрация: 16.06.2005
Сообщений: 944
Сказал спасибо: 25
Сказали Спасибо 174 раз(а) в 123 сообщении(ях)
|
Re: Глюк с UART при засыпании
Сообщение от Godzilla82
|
индексом элемента массива
|
Вообще массивы штука вредная:
1) Необходимость индексной переменной, с которой требуется работать (инициализация, инкремент/декремент, проверка границ и т.п.)
2) В большинстве случаев (на программах более примерно 20-30% объёма контроллера) компиляторы при обращении к массиву каждый раз пересчитывают адрес (т.е. берут адрес начала, к нему прибавляют смещение, а ведь адрес 16 разрядов). И всегда, если работают с данными помеченными как волатиле. Понятное дело, что это и лишнее время, и относительно немалый объём кода.
3) Программисты почему-то в большинстве случаев помимо индекса используют и длину. В этом случае индекс увеличивается вверх, а счётчик вниз. Влечёт за собой дополнительный код и время для работы с ещё одной переменной.
С указателями код и компактней и быстрее.
Сообщение от av0000
|
полный код всего этого
|
1) в функциях uart_putch, uart_getch есть запрет прерываний. А вот разрешения их я там не увидел. Зачем оно вообще там? Не, я конечно могу подумать про атомарность, но мало ли...
2) Зачем сохранять и восстанавливать SREG?
3) Работа с указателями есть. Но почему-то для работы с уарт используются массивы...
4) uart_tx_flush(void). Ну если так хочется писать void uart_tx_flush, то зачем в виде функции? Лишний код и время его выполнения. Может быть стоит оформить в виде макроса?
|
|
|
|
06.09.2013, 12:47
|
#60
|
Почётный гражданин KAZUS.RU
Регистрация: 29.10.2006
Сообщений: 1,443
Сказал спасибо: 99
Сказали Спасибо 315 раз(а) в 231 сообщении(ях)
|
Re: Глюк с UART при засыпании
Сообщение от Someone
|
Вообще массивы штука вредная:
...
С указателями код и компактней и быстрее.
|
Вредная, но удобная. Не вникал в подробности, но всегда думал, что компиляторы (тем более с оптимизацией) делают вполне адекватнй код.
На CV код такой:
PHP код:
|
_usart_tx_isr: ST -Y,R30 ST -Y,R31 IN R30,SREG ST -Y,R30 ; tx_read++; INC R5 ; if(tx_read ›= TX_BUFFER_SIZE) tx_read = 0; LDI R30,LOW(50) CP R5,R30 BRLO _0x3 CLR R5 ; if(tx_read != tx_write) UDR = tx[tx_read]; _0x3: CP R4,R5 BREQ _0x4 MOV R30,R5 LDI R31,0 SUBI R30,LOW(-_tx) SBCI R31,HIGH(-_tx) LD R30,Z OUT 0xC,R30 _0x4: LD R30,Y+ OUT SREG,R30 LD R31,Y+ LD R30,Y+ RETI
|
PHP код:
|
_usart_putchar: ST -Y,R26 ; c -› Y+0 cli ; tx[tx_write] = c; MOV R30,R4 LDI R31,0 SUBI R30,LOW(-_tx) SBCI R31,HIGH(-_tx) LD R26,Y STD Z+0,R26 ; if(tx_write == tx_read) UDR = c; CP R5,R4 BRNE _0x5 LD R30,Y OUT 0xC,R30 ; tx_write++; _0x5: INC R4 ; if(tx_write ›= TX_BUFFER_SIZE) tx_write = 0; LDI R30,LOW(50) CP R4,R30 BRLO _0x6 CLR R4 ; #asm("sei"); _0x6: sei ADIW R28,1 RET
|
С указателями стараюсь не работать, как будет выглядеть через указатели?
Последний раз редактировалось Godzilla82; 06.09.2013 в 13:01.
|
|
|
|
Ваши права в разделе
|
Вы не можете создавать новые темы
Вы не можете отвечать в темах
Вы не можете прикреплять вложения
Вы не можете редактировать свои сообщения
HTML код Выкл.
|
|
|
Часовой пояс GMT +4, время: 01:23.
|
|