|
AVR Надежная работа UART и прерывания прочей переферии, Код UART из DS (реализован без прерываний) и все равно проблемы |
|
|
|
Mar 10 2010, 12:30
|
Частый гость
 
Группа: Участник
Сообщений: 94
Регистрация: 9-04-07
Пользователь №: 26 893

|
Добрый день, столкнулся с такой непонятной для меня ситуацией. Есть ATMega32, которая реализует: ШИМ (1 таймер), импульсы переменной длительности (1 таймер), замеры напряжения с двух каналов АЦП и общение с ПК. Общение с ПК по UART. Протокол общения строго синхронный. Запрос с ПК - ответ с МК. Взял код UART из спецификации на mega32. Если контроллер не загружен - буквально, если выключить АЦП (предделитель 64), а всю остальную переферию оставить (2 таймера), то UART работает нормально - проходит тестирование в 60 тыс. обменов с ПК. Только стоит подключить АЦП начинаются проблемы - ПК фиксирует, что контроллер не отвечает ему за отведенное время. При этом если код функций работы UART заключить в скобки cli-sei, то даже при включенном АЦП, обмен идет хорошо. Если запрещение прерываний убрать, начинаются проблемы. Код UART навсякий случай: Код void usrtSendByte( u08 data ) { cli(); /* Wait for empty transmit buffer */ while ( !( UCSRA & (1<<UDRE)) ) _delay_ms(2); /* Put data into buffer, sends the data */ UDR = data; sei(); }
u08 usrtReadByte( void ) { cli(); /* Wait for data to be received */ while ( !(UCSRA & (1<<RXC)) ) _delay_ms(2); /* Get and return received data from buffer */ u08 temp=UDR; sei(); return temp; } На лицо полное не понимание чего-то  Прошу помощи. Спасибо
Сообщение отредактировал rezident - Mar 10 2010, 14:12
Причина редактирования: оформление цитаты исходника.
|
|
|
|
|
 |
Ответов
|
Mar 14 2010, 12:30
|
Частый гость
 
Группа: Участник
Сообщений: 94
Регистрация: 9-04-07
Пользователь №: 26 893

|
Цитата(aaarrr @ Mar 10 2010, 15:47)  Зачем, если очевидно, что виновато прерывание АЦП? Его и приведите. Написал минимальный код при котором проявляется проблема: CODE #include <avr/io.h> #include <avr/interrupt.h> // Code compatibility to new AVR-libc // outb(), inb(), inw(), outw(), BV(), sbi(), cbi(), sei(), cli() #ifndef outb #define outb(addr, data) addr = (data) #endif #ifndef inb #define inb(addr) (addr) #endif #ifndef inw #define inw(addr) (addr) #endif #ifndef BV #define BV(bit) (1<<(bit)) #endif #ifndef cbi #define cbi(reg,bit) reg &= ~(BV(bit)) #endif #ifndef sbi #define sbi(reg,bit) reg |= (BV(bit)) #endif #ifndef cli #define cli() __asm__ __volatile__ ("cli" :  #endif #ifndef sei #define sei() __asm__ __volatile__ ("sei" :  #endif //***************************************************************************** // // TIMERS // //***************************************************************************** void initTimer() { outb(TCNT0, 0); // reset TCNT0 sbi(TIMSK, TOIE0); // enable TCNT0 overflow interrupt outb(TCCR0, (inb(TCCR0) & ~0x07) | 0x02); outb(TCNT1H, 0); // reset TCNT1 outb(TCNT1L, 0); sbi(TIMSK, TOIE1); // enable TCNT1 overflow outb(TCCR1B, (inb(TCCR1B) & ~0x07) | 0x02); outb(TCNT2, 0); // reset TCNT2 sbi(TIMSK, TOIE2); // enable TCNT2 overflow outb(TCCR2, (inb(TCCR2) & ~0x07) | 0x02); } //! Interrupt handler for tcnt0 overflow interrupt ISR(TIMER0_OVF_vect) { PORTD=0xFF; } //! Interrupt handler for tcnt1 overflow interrupt ISR(TIMER1_OVF_vect) { } //! Interrupt handler for tcnt2 overflow interrupt ISR(TIMER2_OVF_vect) { } //***************************************************************************** // // UART // //***************************************************************************** #define BAUD_RATE 9600ul #define USART_UBBR_VALUE ((F_CPU/(BAUD_RATE<<4))-1) void usrtInit( ) { /* Set baud rate */ UBRRH = (unsigned char)(USART_UBBR_VALUE>>8); UBRRL = (unsigned char)USART_UBBR_VALUE; /* Enable Receiver and Transmitter */ UCSRB = (1<<RXEN)|(1<<TXEN); /* Set frame format: 8data, 2stop bit */ UCSRC = (1<<URSEL)|(1<<USBS)|(3<<UCSZ0); } void usrtSendByte( unsigned char data ) { /* Wait for empty transmit buffer */ while ( !( UCSRA & (1<<UDRE)) ) ; /* Put data into buffer, sends the data */ UDR = data; } unsigned char usrtReadByte( void ) { /* Wait for data to be received */ while ( !(UCSRA & (1<<RXC)) ) ; /* Get and return received data from buffer */ return UDR; } //***************************************************************************** // // ADC // //***************************************************************************** // initialize a2d converter void a2dInit(void) { sbi(ADCSRA, ADEN); // enable ADC (turn on ADC power) sbi(ADCSRA, ADATE); // choose free running mode outb(ADCSRA, ((inb(ADCSRA) & ~0x07) | 0x06)); outb(ADMUX, ((inb(ADMUX) & ~0xC0) | (0x01<<6))); cbi(ADMUX, ADLAR); // set to right-adjusted result outb(ADMUX, (inb(ADMUX) & ~0x1F) | (0 & 0x1F)); // set channel sbi(ADCSRA, ADIE); // enable ADC interrupts } // start a conversion on the current a2d input channel void a2dStartConvert(void) { sbi(ADCSRA, ADIF); // clear hardware "conversion complete" flag sbi(ADCSRA, ADSC); // start conversion } //! Interrupt handler for ADC complete interrupt. SIGNAL(SIG_ADC) { unsigned char sample=(inb(ADCL) | (inb(ADCH)<<8)) >>2; // n?eoaai iiia?yiiia 10-aeoiia cia?aiea e ioa?inei 2 ieaaoeo aeoa } //***************************************************************************** // // MAIN // //***************************************************************************** int main() { sei(); DDRD=0xFF; initTimer(); a2dInit(); a2dStartConvert(); unsigned char data; usrtInit( ); while(1) { data=usrtReadByte(); usrtSendByte(data); } } Код для ПК (на C#): Код private static void VerySimpleEchoTest() { port = new SerialPort("COM1", 9600, Parity.None, 8, StopBits.Two); port.Open();
port.DiscardInBuffer(); port.DiscardOutBuffer(); port.ReadTimeout = 50; int i = 0; while (true) { i++; port.Write(new byte[] { (byte)random.Next(0, 255) }, 0, 1); Console.WriteLine(" echo: " + port.ReadByte()); Console.WriteLine("# " + i); } } Этот код падает при пересылке ~30000 сообщений (5-10 минут работы). При этом до момента падения пересылаемое значение совпадает с ответом из AVR. Контроллер ATMega32A-PU. Связь с ПК через Max232 (осциллограф каких-то проблем (завалы) на сигнальных линиях UART не показывает). Питание 5В от компьютерного БП. Компилятор WinAVR-20090313, AVRStudio 4, -0s. Фреймирование с помощью байтов со значениями 0, 0xFF соответственно до и после послыки байта данных пробовал - не помогает. Цитата(aaarrr @ Mar 10 2010, 15:47)  P.S. _delay_ms(2) зачем? Последствия танцев с бубном. Никак не пойму, где же быть ошибке... для удобства тот же код, только с подсветкой: Код для AVR: http://pastebin.com/43J3unFwКод для ПК: http://pastebin.com/ZJdip1ft
|
|
|
|
|
Mar 14 2010, 18:52
|
Частый гость
 
Группа: Участник
Сообщений: 149
Регистрация: 2-06-08
Из: Москва
Пользователь №: 38 003

|
Цитата(Didro @ Mar 14 2010, 15:30)  Написал минимальный код при котором проявляется проблема: Компилировать Ваш код и смотреть асм. листинг желания нет, уж больно длинный. рекомендации: а) написать действительно минимальный код - если проблема в АЦП, оставить только код относящийся к УАРТУ И АЦП б) оформить обработчик прерывания от АЦП аналогично остальным, при этом не извращаться при чтении АЦП, а сделать так Код ISR(ADC_vect){ unsigned char sample = ADCW; /*Я поначалу не стал заводить лишнюю переменную и написал просто ADCW; но скомпилив увидел, что там в обработчике прерывания какой-то rcall нарисовался, ну его нафиг*/ } в) на мой взгляд выражения типа outb(TCCR1B, (inb(TCCR1B) & ~0x07) | 0x02) не добавляют читаемости кода, наверное лучше так TCCR1B = (TCCR1B&0xf8)|(1<<CS11) Да, кстати, может это и новый стиль, но я чего-то не понимаю, зачем эти sbi() и outb() мне они глаз режут, по-моему обычным макаром Код TCNT0 = 0; код лучше выглядит. ЗЫ ~30000 это случайно не 32767?
Сообщение отредактировал smac - Mar 14 2010, 18:57
|
|
|
|
|
Mar 15 2010, 09:39
|
Частый гость
 
Группа: Участник
Сообщений: 94
Регистрация: 9-04-07
Пользователь №: 26 893

|
Цитата(smac @ Mar 14 2010, 21:52)  Компилировать Ваш код и смотреть асм. листинг желания нет, уж больно длинный. рекомендации: а) написать действительно минимальный код - если проблема в АЦП, оставить только код относящийся к УАРТУ И АЦП Сделано - минимальный код только АЦП и UART: http://pastebin.com/BGZUMdz9Вот asm-листинг этого кода при компиляции с ключом -Os: http://pastebin.com/9sZTuTXSВот asm-листинг этого кода при компиляции с ключом -O1: http://pastebin.com/d5VbNSL8При компиляции с ключом -O1 проблема не наблюдается (версия WinAVR от 20100110), при компиляции с ключом -Os стабильно падает - проблем в asm коде не обнаружил (видимо что-то упускаю) Цитата(smac @ Mar 14 2010, 21:52)  ЗЫ ~30000 это случайно не 32767? Нет падения происходят в различное время (от 7 тыс до 50 тыс).
|
|
|
|
|
Mar 20 2010, 23:46
|

кекс
     
Группа: Свой
Сообщений: 3 825
Регистрация: 17-12-05
Из: Киев
Пользователь №: 12 326

|
Цитата(Didro @ Mar 15 2010, 11:39)  Нет падения происходят в различное время (от 7 тыс до 50 тыс). Вероятно на ПК что-то тормозит как всегда. В программе на PC с портом лучше работать напрямую без сторонних компонентов, т.к. непонятно что и как они делают. Не пользуйте USB<>COM переходники для тестов надежности, потому что это зло которое неадкватно воспринимает параметры COM TIMEOUTS, вследствие чего могут теряться символы. По коду - Вы смотрели, что задает port.ReadTimeout = 50; куда он в итоге пишет? В ComTimeouts->ReadIntervalTimeout или в ComTimeouts->ReadTotalTimeoutConstant? Хотя если используется USB<>COM переходник то оба параметра игнорируются и равны 1ms (по крайней мере с драйверами от FTDI было так). Воспользуйтесь помехоустойчивым протоколом обмена, а на МК реализуйте UART на прерываниях и будет щастье.
|
|
|
|
|
Mar 21 2010, 00:35
|
Частый гость
 
Группа: Участник
Сообщений: 94
Регистрация: 9-04-07
Пользователь №: 26 893

|
Цитата(defunct @ Mar 21 2010, 02:46)  Вероятно на ПК что-то тормозит как всегда. В программе на PC с портом лучше работать напрямую без сторонних компонентов, т.к. непонятно что и как они делают. Не пользуйте USB<>COM переходники для тестов надежности, потому что это зло которое неадкватно воспринимает параметры COM TIMEOUTS, вследствие чего могут теряться символы.
По коду - Вы смотрели, что задает port.ReadTimeout = 50; куда он в итоге пишет? В ComTimeouts->ReadIntervalTimeout или в ComTimeouts->ReadTotalTimeoutConstant? Хотя если используется USB<>COM переходник то оба параметра игнорируются и равны 1ms (по крайней мере с драйверами от FTDI было так). Спасибо за ответ. Вожусь уже не первую неделю с этим делом, готов понаделать всяких параноидальных выводов после многочисленных тестов. Сейчас вот переписал часть на ПК с C# на Си с прямым обращением к COM-порту как к файлу - запустил на тестирование. USB-COM переходники не использовал при тестировании. Цитата(defunct @ Mar 21 2010, 02:46)  Воспользуйтесь помехоустойчивым протоколом обмена, а на МК реализуйте UART на прерываниях и будет щастье. Вы не первый, кто советует переписать в МК UART и сделать его по прерываниям - так и попробую. Но все равно не понимаю, почему должно стать надежнее ? Вроде как оба случая с точки зрения надежности приема байта равнозначны - UART все равно реализован аппаратно и никто из МК не может (в теории) помешать ему принять байт.
|
|
|
|
|
Mar 21 2010, 00:48
|

кекс
     
Группа: Свой
Сообщений: 3 825
Регистрация: 17-12-05
Из: Киев
Пользователь №: 12 326

|
Цитата(Didro @ Mar 21 2010, 02:35)  Вы не первый, кто советует переписать в МК UART и сделать его по прерываниям - так и попробую. Но все равно не понимаю, почему должно стать надежнее ? Вроде как оба случая с точки зрения надежности приема байта равнозначны - UART все равно реализован аппаратно и никто из МК не может (в теории) помешать ему принять байт. Надежнее станет не столько за счет прерываний, сколько за счет помехоустойчивого пакетного протокола (с использованием CRC / FEC / повторов ). Учитывая, что пакетный обмен предполагает прием пакетов состоящих из, пусть минимум 3-х 4-х байт, а приемный FIFO в AVR всего 1 байт. Очевидно что без прерываний можно потерять части пакета, если другая задача (например обработка данных АЦП) будет длиться долго.. Самый простой надежный протокол - эхоплекс. Приемник шлет назад передатчику то, что он принял, передатчик повторяет посылку если обнаруживает ошибку. как реализовать. Допустим вы собираетесь слать на PC данные с АЦП. Пусть по 16 байт в одном пакете. тогда можно сделать такой формат пакета: [ 1 байт sn ][ 16 байт данные с АЦП ] алгоритм МК: 1. Заполняем пакет новыми данными с АЦП. 1. Шлем пакет на PC. 2. Принимаем эхо этого пакета с PC. 3. Если принятный с PC пакет полностью совпадает с тем что отправляли, то увеличиваем sn на 1 и goto 1. 4. иначе goto 2 (шлем тот же пакет без изменений). алгоритм PC: 1. принимаем пакет A; 2. шлем пакет ( A ) обратно; 3. принимаем пакет B; 4. шлем пакет ( B )обратно; 5. если sn( B ) - sn( A ) = 1, то обрабатываем пакет ( A ), и копируем пакет B на место пакета A. 6. гото 3. Теперь думаем как это ускорить, и сделать еще более надежным. Приходим к CRC и к короткому подтверждению (ACK'у) вместо эха, далее к слайд окну, - чтобы МК мог слать следующий пакет не дожидаясь подтвержения текущего и т.д. и т.п. (получаем в итоге нечто похожее на TCP) ;>
|
|
|
|
|
Mar 21 2010, 06:36
|
Частый гость
 
Группа: Участник
Сообщений: 94
Регистрация: 9-04-07
Пользователь №: 26 893

|
Цитата(RodionGork @ Mar 21 2010, 08:03)  Самый простой надежный протокол - эхоплекс. Да, спасибо за подробное описание. Именно "эхоплекс" сейчас и реализован. МК высылает Эхом то, что ему прислали. Но CRC или какие-то ухищрения с протоколом, конкретно мою проблему не решают. У меня ведь посылка\отсылка не зависят от логики пересылаемых значений - пусть даже полезная часть пакета (данные) будет искажена "помехой" полностью - все равно МК её должен принять и тут же выслать назад - посмотите код, он не анализирует даные, просто шлет эхо. Аналогично ситуация обстоит и на ПК. Если уж говорить о предполагаемых причинах, то мне кажутся более вероятными несоответствие baudrate часототе тактирования (по даташиту у меня ~0.2% ошибка (9600bod\16Mhz)) или рассинхронизация по старт-стоповым битам - как это точно диагностировать и как бороться, не понятно. С прерываниями попробую, а вдруг...  Цитата(RodionGork @ Mar 21 2010, 08:03)  //! Interrupt handler for tcnt0 overflow interrupt ISR(TIMER0_OVF_vect) { PORTD=0xFF; } Сигнализировало, что контроллер включен. Цитата(RodionGork @ Mar 21 2010, 08:03)  Ну тогда надо ещё какую-нибудь ерунду спросить. О. осциллятор у вас внешний или что? Внешний, кристал 16МГц.
|
|
|
|
|
Mar 22 2010, 01:26
|

кекс
     
Группа: Свой
Сообщений: 3 825
Регистрация: 17-12-05
Из: Киев
Пользователь №: 12 326

|
Цитата(Didro @ Mar 21 2010, 08:36)  Если уж говорить о предполагаемых причинах, то мне кажутся более вероятными несоответствие baudrate часототе тактирования (по даташиту у меня ~0.2% ошибка (9600bod\16Mhz)) или рассинхронизация по старт-стоповым битам - как это точно диагностировать и как бороться, не понятно. Если после 30-ти тысячного символа у вас искажаются все последующие символы, тогда да - имеет место рассинхронизация, и ошибка у вас не 0.2%, а около 2.5%. Чтобы проверить действительно ли дело в этом - сделайте паузы длиной в 1-2 символа через каждые n символов (пусть n=100). После очередной паузы, синхронизация восстановится. Ошибка в 0.2% - это допустимая ошибка и никак не должна вызывать рассинхронизацию. Рассинхронизацию может вызывать суммарная ошибка в 5% (2.5% приемник, 2.5% в противоположную сторону - передатчик) которая на 10 бит превращается в 50%, т.е. последний передающийся бит читается неверно, ну а затем если передача идет непрерывно, искажаются и все последующие символы.. Цитата(Didro @ Mar 21 2010, 08:36)  У меня ведь посылка\отсылка не зависят от логики пересылаемых значений - пусть даже полезная часть пакета (данные) будет искажена "помехой" полностью - все равно МК её должен принять и тут же выслать назад - посмотите код, он не анализирует даные, просто шлет эхо. Я видел ваш код. Но я не понимаю какая цель ваших экспериментов. Вы считаете, что все каналы связи всегда все передают без ошибок? Так вот смею вас уверить RS232 не гарантирует передачи без потерь. Банально щелкнет UPS - наведется помеха на кабель, которая исказит всего один битик (стартовый битик) и все - приплыли, при непрерывной передаче весь поток будет убит, даже без проблем с синхронизацией.. Если вы хотите добиться надежной передачи, примените помехоустойчивый протокол, с обнаружением и устранением ошибок, и все будет чудесно работать. Цитата(RodionGork @ Mar 21 2010, 09:03)  насчёт того что идеологически программа выглядит правильной и значит должна правильно работать. Она и работает правильно, кто сказал, что нет? Сбоит часть на PC, небольшая задержка вводит PC в ступор, скорее всего просто что-то не так с COM TIMEOUTS как уже говорил выше.. Символ приходит, но по какой-то причине просто заданный таймаут в 50ms срабатывает раньше. Цитата P.S. Не надо переписывать программу. RS232 не гарантирует передачу данных без потерь, поэтому программу все равно придется доработать с учетом этой особенности используемого интерфейса.
|
|
|
|
|
Mar 22 2010, 06:40
|

Местный
  
Группа: Участник
Сообщений: 239
Регистрация: 30-10-07
Из: Санкт-Петербург
Пользователь №: 31 866

|
Цитата(defunct @ Mar 22 2010, 04:26)  Если после 30-ти тысячного символа у вас искажаются все последующие символы, тогда да - имеет место рассинхронизация, и ошибка у вас не 0.2%, а около 2.5%. Чтобы проверить действительно ли дело в этом - сделайте паузы длиной в 1-2 символа через каждые n символов (пусть n=100). После очередной паузы, синхронизация восстановится.
Ошибка в 0.2% - это допустимая ошибка и никак не должна вызывать рассинхронизацию. Рассинхронизацию может вызывать суммарная ошибка в 5% (2.5% приемник, 2.5% в противоположную сторону - передатчик) которая на 10 бит превращается в 50%, т.е. последний передающийся бит читается неверно, ну а затем если передача идет непрерывно, искажаются и все последующие символы.. Э-э-э... Да ну. У вас что, рассинхронизация накапливается со временем? Вы это как представляете? ;-))) Для UART синхронизация каждого байта происходит отдельно по фронту стартового бита. Разве не так? Отсюда и берутся 5%. Если передаются 10 бит (start-8-stop) то последний не должен сдвинуться более чем на половину собственной величины, т.е. на 1/20 всего времени передачи байта. Цитата Цитата P.S. Не надо переписывать программу.
RS232 не гарантирует передачу данных без потерь, поэтому программу все равно придется доработать с учетом этой особенности используемого интерфейса. Да это разумеется, верхний уровень передачи ещё предстоит обдумывать поскольку в существующем виде там не остаётся времени для работы программы ;-) Но проблемы, указанные автором темы свидетельствуют о том что надо что-то в нижнем уровне отыскать сначала странное. А потом переписывать. ;-)))
|
|
|
|
|
Mar 22 2010, 08:55
|
Частый гость
 
Группа: Участник
Сообщений: 94
Регистрация: 9-04-07
Пользователь №: 26 893

|
Вчера обнаружил неправильно запрограммированный fuse CKOPT. Как говорится, и как я его раньше не замечал. Теперь работает стабильно. Всем большое спасибо за поддержку !
|
|
|
|
|
Mar 23 2010, 05:37
|

Местный
  
Группа: Участник
Сообщений: 239
Регистрация: 30-10-07
Из: Санкт-Петербург
Пользователь №: 31 866

|
Цитата(Didro @ Mar 22 2010, 11:55)  Вчера обнаружил неправильно запрограммированный fuse CKOPT. Как говорится, и как я его раньше не замечал. Теперь работает стабильно. Всем большое спасибо за поддержку !  Прикольно. Всё-таки осциллятор... ;-))) Спасибо что не поленились таки найти источник бед. А то уж тут столько предложений у нас в народе народилось (не имевших отношения к реальной проблеме, как видим). Цитата читайте, вникайте.. Не, спасибо... Я как бы и так немножко в теме... ;-)
|
|
|
|
1 чел. читают эту тему (гостей: 1, скрытых пользователей: 0)
Пользователей: 0
|
|
|