|
STM32F10x, не перестаёт удивлять |
|
|
|
Feb 6 2011, 20:02
|
Частый гость
 
Группа: Участник
Сообщений: 183
Регистрация: 22-06-05
Из: Таганрог
Пользователь №: 6 233

|
Полдня чесал репу и разлохматил бубен на казалоь бы ровном месте... А именно: имеем некий код в основном цикле, с временем исполнения, определяемым периодом таймера. Таймер запускается перед контролируемым кодом, тот в цикле делает свои дела, а выходит по флагу, к-й устанавливается в обработчике прерывания (Update Event) этого таймера. Таймер запускается перед этим блоком. По так и невыясненным причинам, всё работало после ресета до того момента, пока не происходило некое внешнее прерывание (с приоритетом ниже таймера), делающее быстро другие примитивные дела, не относящиеся к интересующему участку кода. После этого начинались чудеса - на очередном внешнем цикле после запуска таймера код лихо проскакивал проверку while(!bStopDSS), даже не заглянув внутрь, и происходило 2! прерывания от таймера, одно из которых видимо и устанавливало bStopDSS=1 до входа во внутренний цикл. Вылечил проверкой флага прерывания перед включением таймера. Перекорячивается конвейер команд? При включении таймера (строка TIM3->CR1 |= TIM_CR1_CEN) ещё не сброшен бит прерывания? Код volatile uint8_t bStopDSS; while(1) // внешний цикл { bStopDSS = 0; TIM3->CNT = 0; TIM3->SR = ~TIM_IT_Update; while(TIM3->SR & TIM_IT_Update); // без этой проверки - чудеса TIM3->CR1 |= TIM_CR1_CEN;
while(bStopDSS == 0) { // Внутренний цикл, выходим по таймеру. } // ...готовимся к следующему циклу // }
void TIM3_IRQHandler (void) { // Сбрасываем флаг прерывания TIM3->SR = ~TIM_IT_Update; // Останавливаем таймер TIM3->CR1 &= ~TIM_CR1_CEN; // Флаг окончания внутреннего цикла bStopDSS = 1; } Такие дела. В голове смятение.
Сообщение отредактировал pr0m - Feb 6 2011, 20:05
--------------------
Правильно поставленный вопрос - половина ответа...
|
|
|
|
|
 |
Ответов
|
Feb 7 2011, 06:06
|

фанат дивана
     
Группа: Свой
Сообщений: 3 387
Регистрация: 9-08-07
Из: Уфа
Пользователь №: 29 684

|
Цитата(pr0m @ Feb 7 2011, 01:02)  Такие дела. В голове смятение. Попробуйте проверять источник прерывания перед сбросом: Код void TIM2_IRQHandler(void) { if (TIM2->SR & TIM_SR_UIF) { TIM2->SR = ~TIM_SR_UIF; ... } }
--------------------
Если бы я знал, что такое электричество...
|
|
|
|
|
Feb 7 2011, 08:42
|
Частый гость
 
Группа: Участник
Сообщений: 183
Регистрация: 22-06-05
Из: Таганрог
Пользователь №: 6 233

|
Цитата(AHTOXA @ Feb 7 2011, 09:06)  Попробуйте проверять источник прерывания перед сбросом: Код void TIM2_IRQHandler(void) { if (TIM2->SR & TIM_SR_UIF) { TIM2->SR = ~TIM_SR_UIF; ... } } Флаг UEV установлен в обоих случаях, смотрел в железе. А как может быть иначе, если прерывание только по нему сконфигурено, поэтому даже не проверяю. Даташиты и примеры молчат по этому поводу. Цитата(akimych @ Feb 7 2011, 00:29)  Интересно то, что после while(TIM3->SR & TIM_IT_Update); bStopDSS все равно не обнуляется и TIM_IT_Update тоже не сбрасывается. В смысле, Вы это сами проверяли? Если так, то значит мне это не приглючилось.. Именно так и происходило. Цитата(akimych @ Feb 7 2011, 00:29)  А таймер как сконфигурирован? Просто Upcounting (инициализирую прескалер, ARR, обнуляю CNT, разрешаю прерывания по UEV и вперёд). Цитата(akimych @ Feb 7 2011, 00:29)  В данном случае наверно имело бы смысл использовать One-pulse mode. Если по прерыванию только выставляется флаг bStopDSS, то можно будет вообще без него обойтись. Да, вчера название режима привлекло, но ещё не пробовал. Попробую. По поводу использования bStopDSS отдельная песня - внутренний блок очень критичен по времени (чем быстрее, тем лучше), причём это время должно быть детерминировано. После выяснений-экспериментов сделал следующие выводы: 1) Для детерминированности времени исполнения код должен исполняться из ОЗУ, что и сделал. Хотя исполняется медленнее, чем из флэш - что подтверждает высказывания по поводу отсутствия преимуществ исполнения кода из ОЗУ в сравнении с флэш для STM32F10... 2) Проверка флага UEV как признака окончания внутреннего цикла (while(!(TIMx->CR1 & UEV))увеличивает общее время исполнения блока на 2 такта, в сравнении c флагом в ОЗУ (bStopDSS), что в принципе понятно - чтение регистра+наложение маски бита UEV-сравнение, вместо чтение-сравнение). Поэтому bStopDSS.
Сообщение отредактировал pr0m - Feb 7 2011, 08:44
--------------------
Правильно поставленный вопрос - половина ответа...
|
|
|
|
|
Feb 7 2011, 12:20
|
Частый гость
 
Группа: Участник
Сообщений: 183
Регистрация: 22-06-05
Из: Таганрог
Пользователь №: 6 233

|
Цитата(AHTOXA @ Feb 7 2011, 12:16)  Мдя, спасибо за ссылку. Понял, как нужно, но не понял, зачем. Многовато туману. Где ещё ожидают грабли? А то у меня ещё один необъяснимый и сильно напрягающий случай имеется - тоже таймер, тоже прерывание (с частотой 100кГц), в котором данные с группы из 6 каналов АЦП (АЦП в непрерывном режиме, с использованием ДМА в CircularMode, собирает данные в фиксированное место 6*2 байт) аккуратненько складывает эти 6 отсчётов в большой накопительный буфер, с инкрементом указателя ессно. Если перевести в требуемую скорость по каналу передачи 100кГц*6каналов*16бит = 9600кБит/c. Как только заполнен буфер, нужно его отправлять. Отправляю по Ethernet-у при помощи lwIP udp_send(). Пробовал 2 варианта: 1) В основном цикле программы (от чего очень хотелось бы избавиться, т.к. там коду есть чем другим заняться). Помечаем буфер как готовый для отправки, в основном цикле флажок готовности проверяется, буфер через lwIP udp_send() отправляется. Скорость наблюдаю на приёмном конце - на компе - соответсвует расчётной. п.с. Процесс отправки чуть сложнее описанного, но сути не меняет - используются очереди, процедуры push в обработчике и "пока pop != 0" в основном цикле. 2) Поручаем отправку программному прерыванию: Код void SamplesMain(void) // Вариант1 - процедура отправки в основном цикле { #ifndef INT_TXR QUEUE_ELEMENT* pbuf; while( (pbuf = QueuePopSafe(&readyBufsQueue)) != NULL ) { p->payload = pbuf->pSamples; udp_send(upcb, p); QueuePushSafe(&freeBufsQueue,pbuf); } #endif }
void TIM2_IRQHandler (void) { TIM2->SR = ~TIM_IT_Update;
*(((u32*)pNextSamplesSet)+0) = *(((u32*)&samples_acc)+0); *(((u32*)pNextSamplesSet)+1) = *(((u32*)&samples_acc)+1); *(((u32*)pNextSamplesSet)+2) = *(((u32*)&samples_acc)+2); if(++pNextSamplesSet == (pSamplesAccumulator->pSamples + SAMPLES_SETS_PER_BUF)) { QueuePushUnsafe(&readyBufsQueue,pSamplesAccumulator); #ifdef INT_TXR EXTI->SWIER |= 0x0001; #endif pSamplesAccumulator = QueuePopUnsafe(&freeBufsQueue); if( pSamplesAccumulator == NULL ) { while(1);// Отладка. Сюда вваливаемся навсегда, если буферы уходят медленнее, чем заполняются. } pNextSamplesSet = pSamplesAccumulator->pSamples; } }
void EXTI0_IRQHandler(void)// Вариант2 - процедура отправки в программном прерывании { QUEUE_ELEMENT* pbuf; EXTI->PR = EXTI_Line0;
while( (pbuf = QueuePopSafe(&readyBufsQueue)) != NULL ) { p->payload = pbuf->pSamples; udp_send(upcb, p); QueuePushSafe(&freeBufsQueue,pbuf); } time_elapsed = LocalTime - time; //отладка ++bufs;//отладка // bufs_per_ms = bufs/LocalTime; // kBs = bufs_per_ms * 1200UL * 1000UL; } Так вот при отправке через программное прерывание скорость передачи падает! примерно до 9200кБит. Как такое может быть? Ведь скорость однозначно определяется периодом работы таймера. При этом переполнения/опустошения очередей буферов не происходит. Ощущение, что замедляется бег таймера, как ни идиотски это звучит. Если закомментировать строчку udp_send(upcb, p);, всё приходит в норму (ориентируюсь по кол-ву отправленных буферов bufs за период времени time_elapsed). Ещё не до конца расковырял udp_send, но в конечном итоге она через memcpy копирует данные в буфер для Ethernet-а и отмечает к отправке. Чем может отличаться исполнение кода в конексте прерывания от кода в контексте главного цикла, что может приводить к такому эфф(де)фекту?
Сообщение отредактировал pr0m - Feb 7 2011, 12:24
--------------------
Правильно поставленный вопрос - половина ответа...
|
|
|
|
Сообщений в этой теме
pr0m STM32F10x Feb 6 2011, 20:02 akimych Интересно то, что после while(TIM3->SR & TI... Feb 6 2011, 21:29 pr0m Победил. Не была задана группировка с вытеснением ... Feb 7 2011, 14:18 sonycman Цитата(pr0m @ Feb 7 2011, 17:18) Задал пр... Feb 7 2011, 15:21  pr0m Цитата(sonycman @ Feb 7 2011, 18:21) Так ... Feb 7 2011, 15:39   sonycman Цитата(pr0m @ Feb 7 2011, 18:39) Высший-т... Feb 7 2011, 16:04    AHTOXA Цитата(sonycman @ Feb 7 2011, 21:04) По у... Feb 7 2011, 16:17     sonycman Цитата(AHTOXA @ Feb 7 2011, 19:17) Ненене... Feb 7 2011, 16:25      pr0m Цитата(sonycman @ Feb 7 2011, 19:25) Так ... Feb 7 2011, 16:57       sonycman Цитата(pr0m @ Feb 7 2011, 19:57) Нет, одн... Feb 7 2011, 17:11        pr0m Цитата(sonycman @ Feb 7 2011, 20:11) Хм, ... Feb 7 2011, 17:17       AHTOXA Цитата(pr0m @ Feb 7 2011, 21:57) Нет, одн... Feb 7 2011, 17:15        sonycman Цитата(AHTOXA @ Feb 7 2011, 20:15) Всё же... Feb 7 2011, 17:22        pr0m Цитата(AHTOXA @ Feb 7 2011, 20:15) Всё же... Feb 7 2011, 17:31         AHTOXA Цитата(pr0m @ Feb 7 2011, 22:22) А моя пр... Feb 7 2011, 17:32         KnightIgor Цитата(pr0m @ Feb 7 2011, 18:31) А моя пр... Feb 7 2011, 17:58          pr0m Цитата(KnightIgor @ Feb 7 2011, 20:58) По... Feb 7 2011, 18:05      AHTOXA Цитата(sonycman @ Feb 7 2011, 21:04) По у... Feb 7 2011, 17:06 pr0m Разобрался. sonycman прав. 15 подгрупп после ресет... Feb 7 2011, 17:53 sonycman Цитата(pr0m @ Feb 7 2011, 20:53) И что ж ... Feb 7 2011, 18:05  pr0m Цитата(sonycman @ Feb 7 2011, 21:05) В эт... Feb 7 2011, 18:11 akimych Цитата1) Для детерминированности времени исполнени... Feb 7 2011, 19:06 pr0m Цитата(akimych @ Feb 7 2011, 22:06) Стран... Feb 7 2011, 19:31 akimych ЦитатаВ этой ветке переплелись мои вопросы по двум... Feb 7 2011, 21:24 pr0m Цитата(akimych @ Feb 8 2011, 00:24) На сч... Feb 8 2011, 07:03  ReAl Цитата(pr0m @ Feb 8 2011, 09:03) Там долж... Feb 8 2011, 23:27 akimych ЦитатаВы наверное имеете ввиду доступ к bit-bandin... Feb 8 2011, 22:00 pr0m Цитата(akimych @ Feb 9 2011, 01:00) А что... Feb 9 2011, 16:48  ReAl Цитата(pr0m @ Feb 9 2011, 18:48) Не знаю.... Feb 9 2011, 21:08   pr0m Цитата(ReAl @ Feb 10 2011, 00:08) Значени... Feb 10 2011, 06:06    vmp Цитата(pr0m @ Feb 10 2011, 09:06) Спасибо... Feb 10 2011, 07:39     pr0m Цитата(vmp @ Feb 10 2011, 10:39) Преимуще... Feb 10 2011, 12:19    ReAl Цитата(pr0m @ Feb 10 2011, 08:06) Спасибо... Feb 10 2011, 14:07     pr0m Цитата(ReAl @ Feb 10 2011, 17:07) Проблем... Feb 10 2011, 21:48 pr0m Красиво, получается, но основной недостаток - чуть... Feb 11 2011, 14:31 akimych В принципе, запись в буфер и запись в регистр ЦАП-... Feb 13 2011, 02:17 pr0m Цитата(akimych @ Feb 13 2011, 05:17) Изме... Feb 13 2011, 07:03
2 чел. читают эту тему (гостей: 2, скрытых пользователей: 0)
Пользователей: 0
|
|
|