alex1985 0 15 февраля, 2012 Опубликовано 15 февраля, 2012 · Жалоба Доброго всем времени суток! Работаю на stm32f103ve в keil'е. Хочу сформировать функцию задержки. Особая точность не нужна - в пределах 1мкс вполне достаточно. Делаю так: Настраиваю таймер: TIM5->ARR = 0xFFFF; TIM5->PSC = 71; //72МГцчастота шины/72 = 1Мгц тик таймера TIM5->CR1 = 0x0001; //Запускаю таймер В функции задаю значение задержки в ARR и опрашиваю флаг: void Delay(u16 time) { TIM5->CNT = 0x0000; TIM5->ARR = time-1; while(!(TIM5->SR & 0x0001)) { __nop(); } TIM5->SR&= ~0x0001; } Вобщем этот код работает только когда я вставляю его в основной цикл. Проверяю так: GPIOE->BSRR|= GPIO_Pin_2; //Установить ногу Delay(10); //Задержка 10мкс. GPIOE->BRR|= GPIO_Pin_2; //Сбросить ногу Если я использую этот код в обработчике нажатия кнопки, то пауза куда-то исчезает и вместо положенных 10 мкс на осцилографе наблюдается 1,2мкс. Есть подозрение, что компилятор каким-то образом оптимизирует код, исключая цикл - другого объяснения у меня нет. Уровень оптимизации - O0 (пробовал O1, O2-результат тот же). Пробовал также объявлять параметр фукции time как volatile - не помогло. Уже ветает шальная мысль написать функцию задержки на asm :( Есть ли у кого какие соображения? Может есть директивы компилятора типа: отключить оптимизацию ... код ... включить оптимизацию Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Сергей Борщ 141 15 февраля, 2012 Опубликовано 15 февраля, 2012 · Жалоба Пробовал также объявлять параметр фукции time как volatile - не помогло. Уже ветает шальная мысль написать функцию задержки на asm :( Есть ли у кого какие соображения? Может есть директивы компилятора типа:Не нужно гадать. Если вы чувствуете в себе силы написать эту функцию на асме - для вас не составит огромного труда посмотреть листинг этой функции и убедиться, что компилятор тут не при чем. Убедиться, что и volatile в параметре функции не нужен, и __nop() лишний, и компилить без оптимизации - прятать голову в песок. Есть большое подозрение, что для борьбы с "чудесами" достаточно поставить сброс флага переполнения непосредственно перед циклом. А после цикла можно убрать. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
alex1985 0 16 февраля, 2012 Опубликовано 16 февраля, 2012 · Жалоба Есть большое подозрение, что для борьбы с "чудесами" достаточно поставить сброс флага переполнения непосредственно перед циклом. А после цикла можно убрать. ;) Вы правы - это все невнимательность. Тема закрыта. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
ViKo 1 20 февраля, 2012 Опубликовано 20 февраля, 2012 · Жалоба Есть большое подозрение, что для борьбы с "чудесами" достаточно поставить сброс флага переполнения непосредственно перед циклом. А после цикла можно убрать. Есть нюанс - если сброс флага будет перед циклом проверки, то если вдруг после загрузки счетчика произошло прерывание на длительное время, и счетчик уже успел отсчитать до выхода из прерывания, а после этого мы сбрасываем флаг переполнения и ждем... следующего переполнения. У меня используется однократный режим, вообще не дождаться. :) TIM6->PSC = (72000000 / 10000 - 1); TIM6->CR1 = TIM_CR1_OPM | TIM_CR1_URS; void Timer_delay(uint32_t time) { TIM6->ARR = time; // Autoreload TIM6->CR1 |= 0x0001; // Enable while (!TIM6->SR); // Wait Update Interrupt Flag in Status TIM6->SR = 0; // Status reset } upd. А еще у меня таймер считает в 2 раза быстрее! По осциллографу вижу. Сижу, разбираюсь, горюю. upd2. Код исправил. Спасибо, АНТОХА! Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
ukpyr 0 20 февраля, 2012 Опубликовано 20 февраля, 2012 · Жалоба inline void _delay_loops(U32 loops) { asm volatile ( "1: SUBS %[loops], %[loops], #1 \n" " BNE 1b \n" : [loops] "+r"(loops) ); } #define delay_us( US ) _delay_loops( (U32)((double)US * F_CPU / 3000000.0) ) #define delay_ms( MS ) _delay_loops( (U32)((double)MS * F_CPU / 3000.0) ) #define delay_s( S ) _delay_loops( (U32)((double)S * F_CPU / 3.0) ) Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
ViKo 1 20 февраля, 2012 Опубликовано 20 февраля, 2012 · Жалоба Для ukpyr А вас прерывания и наличие буфера команд не смущают? Цифры проверяли на железе? Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
AHTOXA 18 20 февраля, 2012 Опубликовано 20 февраля, 2012 · Жалоба А еще у меня таймер считает в 2 раза быстрее! По осциллографу вижу. Сижу, разбираюсь, горюю. Оно: ? Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
ViKo 1 20 февраля, 2012 Опубликовано 20 февраля, 2012 · Жалоба Оно: ? Нет, похоже, прескалер для APB1 не задан /2. Библиотечная функция... туды ее в качель. Шукаю... Да, вы правы! Оно! Не сообразил, что 36 MHz - это уже поделенное прескалером на 2. А потом умноженное. Подправлю код, что выдал раньше. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
ViKo 1 21 февраля, 2012 Опубликовано 21 февраля, 2012 · Жалоба Подправил код, перенес сброс запроса перед запуском таймера (и на две части разбил, между которыми можно не просто ждать, а сделать что-нибудь полезное). Раньше в отладчике подобная конструкция показывала неправильную работу. Но дело было в отладчике. Просто в нем при пошаговом выполнении таймер свою работу не прекращал. Только запустил - глядь, на следующем шаге уже все готово. :) На реальном железе работает, как положено. TIM6->PSC = (72000000 / 10000 - 1); // Prescaler 10 kHz, 0.1 ms TIM6->CR1 = TIM_CR1_OPM | TIM_CR1_URS; void Timer_start(uint32_t time) { TIM6->SR = 0; // Status reset TIM6->ARR = time; // Autoreload (one pulse) TIM6->CR1 |= 0x0001; // Enable } void Timer_wait() { while (!TIM6->SR); // Wait Update Interrupt Flag in Status } Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
esaulenka 7 22 февраля, 2012 Опубликовано 22 февраля, 2012 · Жалоба В очередной раз порекламирую другой подход к использованию таймера: typedef uint32_t TTimer; #define TIMER_COUNTER LPC_TMR32B0->TC #define START_TIMER(tmr) tmr = TIMER_COUNTER #define TIMER_VALUE(tmr) (TIMER_COUNTER - tmr) #define WAIT_TIMEOUT(tmr,val) while (!(val <= TIMER_VALUE (tmr))) __inline void delay_us (TTimer val) { START_TIMER (TTimer Tmr); WAIT_TIMEOUT (Tmr, val); } __inline void delay_ms (TTimer val) { delay_us (val * 1000); } Используется 32-битный таймер в LPC. Таймер настраивается на частоту 1 МГц и ни разу не сбрасывается. Никто не мешает при этом на тот же таймер вешать какие-то другие прерывания. Для таймеров STM надо поменять typedef TTimer на 16-битный, чтобы эта переменная переполнялась вместе с таймером. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
ukpyr 0 22 февраля, 2012 Опубликовано 22 февраля, 2012 (изменено) · Жалоба А вас прерывания и наличие буфера команд не смущают? Цифры проверяли на железе?перед _delay_loops прерывания нужно запрещать, если они возможны. Цифры проверены неоднократно на реальных проектах - тело цикла выполняется за 3 такта. Проверялось также на проекте с несколькими шинами 1-wire под Protothreads Изменено 22 февраля, 2012 пользователем ukpyr Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
ViKo 1 29 февраля, 2012 Опубликовано 29 февраля, 2012 · Жалоба typedef uint32_t TTimer; #define TIMER_COUNTER LPC_TMR32B0->TC #define START_TIMER(tmr) tmr = TIMER_COUNTER #define TIMER_VALUE(tmr) (TIMER_COUNTER - tmr) #define WAIT_TIMEOUT(tmr,val) while (!(val <= TIMER_VALUE (tmr))) __inline void delay_us (TTimer val) { START_TIMER (TTimer Tmr); WAIT_TIMEOUT (Tmr, val); } Допустим, TC вот-вот переполнится. Мы запоминаем его значение в переменной tmr, и потом ждем на время, пока (TC - tmr) < val. Когда TC переходит в 0, (TC - tmr) превращается в большое число, и ваша функция рапортует о таймауте, которого на самом деле еще нет. А для STM32 с 16-битовыми счетчиками это еще более вероятно. Нужно проверять на равенство, а не на превышение. Но в этом случае нельзя проскочить это равенство, значит, нельзя проверять изредка. То есть, не получится, к примеру, задать несколько задержек на одном таймере, и проверять их по мере необходимости. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
AHTOXA 18 29 февраля, 2012 Опубликовано 29 февраля, 2012 · Жалоба Допустим, TC вот-вот переполнится. Мы запоминаем его значение в переменной tmr, и потом ждем на время, пока (TC - tmr) < val. Когда TC переходит в 0, (TC - tmr) превращается в большое число, и ваша функция рапортует о таймауте, которого на самом деле еще нет. А для STM32 с 16-битовыми счетчиками это еще более вероятно. Нужно проверять на равенство, а не на превышение. Но в этом случае нельзя проскочить это равенство, значит, нельзя проверять изредка. То есть, не получится, к примеру, задать несколько задержек на одном таймере, и проверять их по мере необходимости. Нормально там всё. Допустим, мы хотим проспать 20 тактов (val). 1. Засекаем время начала ожидания: tmr = TMR; 2. Потом начинаем в цикле (или изредка, как угодно) вычислять беззнаковую разность TMR-tmr, которая будет монотонно расти от 0 до 0xFFFF (в случае 16-битного таймера), и сравнивать её с val. 3. Как только значение разности станет больше чем val - готово. Главное - не прозевать 0xFFFF тиков таймера:) Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
ViKo 1 29 февраля, 2012 Опубликовано 29 февраля, 2012 · Жалоба Нормально там всё. Да, получается. Я пытался сделать иначе, чтобы разность все время не вычислять. Допустим, нужна задержка на 0x14 тактов. Читаем начальное значение таймера, к примеру, 0xfff0. Прибавляем к нему нашу задержку, получаем 0x0004. А теперь ждем, когда таймер проскочит этот порог. И... не можем определить. :( Пока таймер досчитает до этого порога, он и очень большие значения будет иметь, пока до переполнения дойдет, и малые. А если постоянно вычислять разность между текущим значением таймера и начальным, то она будет расти монотонно. Почти парадокс. :laughing: Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
esaulenka 7 2 марта, 2012 Опубликовано 2 марта, 2012 · Жалоба Проверил в кейловском симуляторе. Всё нормально работает даже при переходе через 0xffffffff. Как переделать на 16 бит, подумаю. Дизассемблер "проекта" прикладываю. Исходный код (все 10 строчек) легко восстановить. ; generated by ARM C/C++ Compiler with , RVCT4.0 [build 524] for uVision ; commandline ArmCC [--debug -c --asm --interleave -omain.o --depend=main.d --device=DARMP1 --apcs=interwork -O3 -IC:\Keil\ARM\INC\NXP --omf_browse=main.crf main.c] THUMB AREA ||.text||, CODE, READONLY, ALIGN=2 main PROC ;;;31 ;;;32 int main (void) 000000 b530 PUSH {r4,r5,lr} ;;;33 { ;;;34 ;;;35 TIM0->CTCR = 0x00; 000002 2000 MOVS r0,#0 000004 f04f2240 MOV r2,#0x40004000 000008 6710 STR r0,[r2,#0x70] ;;;36 TIM0->TCR = 0x02; 00000a 2102 MOVS r1,#2 00000c 6051 STR r1,[r2,#4] ;;;37 TIM0->PR = 0; 00000e 60d0 STR r0,[r2,#0xc] ;;;38 TIM0->TCR = 0x01; 000010 2001 MOVS r0,#1 000012 6050 STR r0,[r2,#4] ;;;39 ;;;40 ;;;41 GPIO0->FIODIR ^= 0x01; 000014 4c08 LDR r4,|L1.56| 000016 6820 LDR r0,[r4,#0] 000018 f0800001 EOR r0,r0,#1 00001c 6020 STR r0,[r4,#0] 00001e f2427510 MOV r5,#0x2710 |L1.34| 000022 6891 LDR r1,[r2,#8] 000024 462b MOV r3,r5 |L1.38| 000026 6890 LDR r0,[r2,#8] 000028 1a40 SUBS r0,r0,r1 00002a 4298 CMP r0,r3 00002c d3fb BCC |L1.38| ;;;42 ;;;43 while (1) ;;;44 { ;;;45 delay_ms (10); ;;;46 ;;;47 GPIO0->FIOPIN ^= 0x01; 00002e 6960 LDR r0,[r4,#0x14] 000030 f0800001 EOR r0,r0,#1 000034 6160 STR r0,[r4,#0x14] 000036 e7f4 B |L1.34| ;;;48 } ;;;49 ;;;50 ; ;;;51 } ENDP |L1.56| DCD 0x2009c000 __ARM_use_no_argv EQU 0 таймер настраивался в режиме "лишь бы тикал" Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться