Didro 0 10 марта, 2010 Опубликовано 10 марта, 2010 (изменено) · Жалоба Добрый день, столкнулся с такой непонятной для меня ситуацией. Есть 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; } На лицо полное не понимание чего-то :) Прошу помощи. Спасибо Изменено 10 марта, 2010 пользователем rezident оформление цитаты исходника. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
aaarrr 69 10 марта, 2010 Опубликовано 10 марта, 2010 · Жалоба Код UART навсякий случай: Зачем, если очевидно, что виновато прерывание АЦП? Его и приведите. P.S. _delay_ms(2) зачем? Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Didro 0 14 марта, 2010 Опубликовано 14 марта, 2010 · Жалоба Зачем, если очевидно, что виновато прерывание АЦП? Его и приведите. Написал минимальный код при котором проявляется проблема: #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 соответственно до и после послыки байта данных пробовал - не помогает. P.S. _delay_ms(2) зачем? Последствия танцев с бубном. Никак не пойму, где же быть ошибке... для удобства тот же код, только с подсветкой: Код для AVR: http://pastebin.com/43J3unFw Код для ПК: http://pastebin.com/ZJdip1ft Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
smac 0 14 марта, 2010 Опубликовано 14 марта, 2010 (изменено) · Жалоба Написал минимальный код при котором проявляется проблема: Компилировать Ваш код и смотреть асм. листинг желания нет, уж больно длинный. рекомендации: а) написать действительно минимальный код - если проблема в АЦП, оставить только код относящийся к УАРТУ И АЦП б) оформить обработчик прерывания от АЦП аналогично остальным, при этом не извращаться при чтении АЦП, а сделать так 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? Изменено 14 марта, 2010 пользователем smac Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Didro 0 15 марта, 2010 Опубликовано 15 марта, 2010 · Жалоба Компилировать Ваш код и смотреть асм. листинг желания нет, уж больно длинный. рекомендации: а) написать действительно минимальный код - если проблема в АЦП, оставить только код относящийся к УАРТУ И АЦП Сделано - минимальный код только АЦП и UART: http://pastebin.com/BGZUMdz9 Вот asm-листинг этого кода при компиляции с ключом -Os: http://pastebin.com/9sZTuTXS Вот asm-листинг этого кода при компиляции с ключом -O1: http://pastebin.com/d5VbNSL8 При компиляции с ключом -O1 проблема не наблюдается (версия WinAVR от 20100110), при компиляции с ключом -Os стабильно падает - проблем в asm коде не обнаружил (видимо что-то упускаю) ЗЫ ~30000 это случайно не 32767? Нет падения происходят в различное время (от 7 тыс до 50 тыс). Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
smac 0 16 марта, 2010 Опубликовано 16 марта, 2010 · Жалоба При компиляции с ключом -O1 проблема не наблюдается (версия WinAVR от 20100110), при компиляции с ключом -Os стабильно падает - проблем в asm коде не обнаружил (видимо что-то упускаю) Что-то я тоже ничего криминального не вижу. Попробуйте все-же оформить обработчик АЦП как ISR(ADC_vect){...} и еще попробуйте сделать следующее в майне сначала настройте уарт, затем настройте АЦП, затем запустите АЦП и только потом глобально разрешите прерывания, т. е. sei() должна стоять непосредственно перед входом в бесконечный цикл. Опишите поподробней в чем выражаются "падения". Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
defunct 0 20 марта, 2010 Опубликовано 20 марта, 2010 · Жалоба Нет падения происходят в различное время (от 7 тыс до 50 тыс). Вероятно на ПК что-то тормозит как всегда. В программе на PC с портом лучше работать напрямую без сторонних компонентов, т.к. непонятно что и как они делают. Не пользуйте USB<>COM переходники для тестов надежности, потому что это зло которое неадкватно воспринимает параметры COM TIMEOUTS, вследствие чего могут теряться символы. По коду - Вы смотрели, что задает port.ReadTimeout = 50; куда он в итоге пишет? В ComTimeouts->ReadIntervalTimeout или в ComTimeouts->ReadTotalTimeoutConstant? Хотя если используется USB<>COM переходник то оба параметра игнорируются и равны 1ms (по крайней мере с драйверами от FTDI было так). Воспользуйтесь помехоустойчивым протоколом обмена, а на МК реализуйте UART на прерываниях и будет щастье. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Didro 0 21 марта, 2010 Опубликовано 21 марта, 2010 · Жалоба Вероятно на ПК что-то тормозит как всегда. В программе на PC с портом лучше работать напрямую без сторонних компонентов, т.к. непонятно что и как они делают. Не пользуйте USB<>COM переходники для тестов надежности, потому что это зло которое неадкватно воспринимает параметры COM TIMEOUTS, вследствие чего могут теряться символы. По коду - Вы смотрели, что задает port.ReadTimeout = 50; куда он в итоге пишет? В ComTimeouts->ReadIntervalTimeout или в ComTimeouts->ReadTotalTimeoutConstant? Хотя если используется USB<>COM переходник то оба параметра игнорируются и равны 1ms (по крайней мере с драйверами от FTDI было так). Спасибо за ответ. Вожусь уже не первую неделю с этим делом, готов понаделать всяких параноидальных выводов после многочисленных тестов. Сейчас вот переписал часть на ПК с C# на Си с прямым обращением к COM-порту как к файлу - запустил на тестирование. USB-COM переходники не использовал при тестировании. Воспользуйтесь помехоустойчивым протоколом обмена, а на МК реализуйте UART на прерываниях и будет щастье. Вы не первый, кто советует переписать в МК UART и сделать его по прерываниям - так и попробую. Но все равно не понимаю, почему должно стать надежнее ? Вроде как оба случая с точки зрения надежности приема байта равнозначны - UART все равно реализован аппаратно и никто из МК не может (в теории) помешать ему принять байт. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
defunct 0 21 марта, 2010 Опубликовано 21 марта, 2010 · Жалоба Вы не первый, кто советует переписать в МК 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) ;> Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
RodionGork 0 21 марта, 2010 Опубликовано 21 марта, 2010 (изменено) · Жалоба //! Interrupt handler for tcnt0 overflow interrupt ISR(TIMER0_OVF_vect) { PORTD=0xFF; } Вот эта штука зачем? Она как-то помогает формированию сигналов UART висящего на нижних битах порта D? А не... Я не прав... когда уарт включен он пины захватывает и по крайней мере помешать это не должно... Ну тогда надо ещё какую-нибудь ерунду спросить. О. осциллятор у вас внешний или что? Изменено 21 марта, 2010 пользователем RodionGork Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Didro 0 21 марта, 2010 Опубликовано 21 марта, 2010 · Жалоба Самый простой надежный протокол - эхоплекс.Да, спасибо за подробное описание. Именно "эхоплекс" сейчас и реализован. МК высылает Эхом то, что ему прислали. Но CRC или какие-то ухищрения с протоколом, конкретно мою проблему не решают. У меня ведь посылка\отсылка не зависят от логики пересылаемых значений - пусть даже полезная часть пакета (данные) будет искажена "помехой" полностью - все равно МК её должен принять и тут же выслать назад - посмотите код, он не анализирует даные, просто шлет эхо. Аналогично ситуация обстоит и на ПК. Если уж говорить о предполагаемых причинах, то мне кажутся более вероятными несоответствие baudrate часототе тактирования (по даташиту у меня ~0.2% ошибка (9600bod\16Mhz)) или рассинхронизация по старт-стоповым битам - как это точно диагностировать и как бороться, не понятно. С прерываниями попробую, а вдруг... :) //! Interrupt handler for tcnt0 overflow interrupt ISR(TIMER0_OVF_vect) { PORTD=0xFF; } Сигнализировало, что контроллер включен. Ну тогда надо ещё какую-нибудь ерунду спросить. О. осциллятор у вас внешний или что?Внешний, кристал 16МГц. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
RodionGork 0 21 марта, 2010 Опубликовано 21 марта, 2010 (изменено) · Жалоба Сигнализировало, что контроллер включен. Внешний, кристал 16МГц. Частота тактирования у вас отличная получается. Должна. Там до 5% допускается если мне не изменяет склероз и сам я неоднократно с гораздо большей ошибкой чем 0.2% работал. Вы можете проверить "рассинхронизацию по старт-стоповым битам" как вы это называете проверяя ошибку Frame Error в UCSRA. Внешний кристалл это хорошо. К этому вопросов больше нет;-))) PORTD всё-таки уберите, попробуйте, если не сложно. UART при подключении перекрывает управление DDRD 1 и 0, но подтягивающим резистором на RX, например, вы всё равно при этом машете. Насчёт TX не знаю. А нет, тоже фигня. Если его один раз установили в 1 (подтяжку) то больше вы её не сбрасываете. Ну, я опять не прав, видимо. Другое дело, что, безусловно, не ясно, причём тут подключение АЦП. а вот cli и sei устанавливаемые для функций UART конечно запрещают таймеру манипулировать портом D. P.S. Не надо переписывать программу. По крайней мере пока ошибка не найдена. Вы правы насчёт того что идеологически программа выглядит правильной и значит должна правильно работать. Если работает неправильно, значит есть нюанс. Если никто не может сказать, в чём он - это ещё не значит, что его нет. И если вы перепишете программу и глюк _вроде бы_ пропадёт - как можно будет знать что от него точно избавились и что он вас не подведёт как-нибудь в дальнейшем. ;-) Изменено 21 марта, 2010 пользователем RodionGork Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
defunct 0 22 марта, 2010 Опубликовано 22 марта, 2010 · Жалоба Если уж говорить о предполагаемых причинах, то мне кажутся более вероятными несоответствие baudrate часототе тактирования (по даташиту у меня ~0.2% ошибка (9600bod\16Mhz)) или рассинхронизация по старт-стоповым битам - как это точно диагностировать и как бороться, не понятно. Если после 30-ти тысячного символа у вас искажаются все последующие символы, тогда да - имеет место рассинхронизация, и ошибка у вас не 0.2%, а около 2.5%. Чтобы проверить действительно ли дело в этом - сделайте паузы длиной в 1-2 символа через каждые n символов (пусть n=100). После очередной паузы, синхронизация восстановится. Ошибка в 0.2% - это допустимая ошибка и никак не должна вызывать рассинхронизацию. Рассинхронизацию может вызывать суммарная ошибка в 5% (2.5% приемник, 2.5% в противоположную сторону - передатчик) которая на 10 бит превращается в 50%, т.е. последний передающийся бит читается неверно, ну а затем если передача идет непрерывно, искажаются и все последующие символы.. У меня ведь посылка\отсылка не зависят от логики пересылаемых значений - пусть даже полезная часть пакета (данные) будет искажена "помехой" полностью - все равно МК её должен принять и тут же выслать назад - посмотите код, он не анализирует даные, просто шлет эхо. Я видел ваш код. Но я не понимаю какая цель ваших экспериментов. Вы считаете, что все каналы связи всегда все передают без ошибок? Так вот смею вас уверить RS232 не гарантирует передачи без потерь. Банально щелкнет UPS - наведется помеха на кабель, которая исказит всего один битик (стартовый битик) и все - приплыли, при непрерывной передаче весь поток будет убит, даже без проблем с синхронизацией.. Если вы хотите добиться надежной передачи, примените помехоустойчивый протокол, с обнаружением и устранением ошибок, и все будет чудесно работать. насчёт того что идеологически программа выглядит правильной и значит должна правильно работать. Она и работает правильно, кто сказал, что нет? Сбоит часть на PC, небольшая задержка вводит PC в ступор, скорее всего просто что-то не так с COM TIMEOUTS как уже говорил выше.. Символ приходит, но по какой-то причине просто заданный таймаут в 50ms срабатывает раньше. P.S. Не надо переписывать программу. RS232 не гарантирует передачу данных без потерь, поэтому программу все равно придется доработать с учетом этой особенности используемого интерфейса. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
RodionGork 0 22 марта, 2010 Опубликовано 22 марта, 2010 · Жалоба Если после 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 не гарантирует передачу данных без потерь, поэтому программу все равно придется доработать с учетом этой особенности используемого интерфейса. Да это разумеется, верхний уровень передачи ещё предстоит обдумывать поскольку в существующем виде там не остаётся времени для работы программы ;-) Но проблемы, указанные автором темы свидетельствуют о том что надо что-то в нижнем уровне отыскать сначала странное. А потом переписывать. ;-))) Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Didro 0 22 марта, 2010 Опубликовано 22 марта, 2010 · Жалоба Вчера обнаружил неправильно запрограммированный fuse CKOPT. Как говорится, и как я его раньше не замечал. Теперь работает стабильно. Всем большое спасибо за поддержку ! :) Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться