STM32F030F4 задержки

Функция delay() при помощи таймера TIM16

Сперва инициализация

void TIM16_init(void){
  NVIC_InitTypeDef NVIC_InitStructure;
  TIM_TimeBaseInitTypeDef TIM_InitStructure; 

  RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM16, ENABLE);

  NVIC_InitStructure.NVIC_IRQChannel = TIM16_IRQn;
  NVIC_InitStructure.NVIC_IRQChannelPriority = 0x01;
  NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
  NVIC_Init(&NVIC_InitStructure);  
  
  TIM_InitStructure.TIM_Period = 1;
  TIM_InitStructure.TIM_Prescaler = 400;
  TIM_InitStructure.TIM_CounterMode = TIM_CounterMode_Up;
  TIM_InitStructure.TIM_ClockDivision = TIM_CKD_DIV1;
  TIM_InitStructure.TIM_RepetitionCounter = 0;
  TIM_TimeBaseInit(TIM16, &TIM_InitStructure);

  TIM_ITConfig(TIM16, TIM_IT_Update, ENABLE);
  TIM_Cmd(TIM16, ENABLE);
}


400 реально мэджик нумбер подобраный осцилографом. )) Как оно рассчитывается в теории понятно, но на практике что-то не очень. Вообщем это дает нам прерывание таймера каждые 100 микросекунд.


static volatile uint16_t delay_counter = 0;
void TIM16_IRQHandler(void) {
    if (TIM_GetITStatus(TIM16, TIM_IT_Update) != RESET) {
        delay_counter++;
        TIM_ClearITPendingBit(TIM16, TIM_IT_Update);
    }
}


И собственно функции задержки:

void delay_100us(void) {    // задержка 100 uS
  delay_counter = 0;
  while (!delay_counter);
}

void delay_ms(int ms) {    // задержка N mS
  int i;
  for(i=0;i<ms*10;i++) {
    delay_100us();
  }
}


Код мне кажется не очень оптимальным, — вот как чувствую грабли, — послушал бы ваши замечания.

Использвание (мигание светодиодом):

  while(1)
  {
     GPIO_SetBits(GPIOA, GPIO_Pin_4);
     delay_ms(500);
     GPIO_ResetBits(GPIOA, GPIO_Pin_4);
     delay_ms(500);
  }
  • -3
  • 15 февраля 2016, 01:23
  • aliaksei

Комментарии (34)

RSS свернуть / развернуть
вот как чувствую грабли, — послушал бы ваши замечания.

1. опримизатор может превратить цикл while (!delay_counter); в бесконечный т. к. нет volatile

2. Прерывания на частоте 100 uS просто впустую съедят много ресурсов МК на вход в прерывание, инкремент переменной и выход.

3. while (!delay_counter); Мы просто можем пропустить момент когда delay_counter == 0 из-за вложенных прерываний.
0
  • avatar
  • e_mc2
  • 15 февраля 2016, 01:58
Я тут про SysTick нагуглил. Похоже им делать задержки более правильно.
0
Исключительно правильно и рекомендуемо самым АРМом. Таймеры они для работы с периферией, а не системных задержек.
0
если RTOS нет — то вроде как наиболее правильно
вот тут неплохо описано
и тут немного

у меня пока из проекта в проект кочует такой код (тактовая 24МГЦ HSE):

#define SYSTICKPERIOD	240	//10мкс
//#define SYSTICKPERIOD	2400	//100мкс
//#define SYSTICKPERIOD	24000	//1мс

#define TICKS_ON_MS	100	//тиков системного таймера в 1мс
//#define TICKS_ON_MS	10	//тиков системного таймера в 1мс
//#define TICKS_ON_MS	1	//тиков системного таймера в 1мс


volatile uint_fast32_t SysCounter; //системный счетчик

//в конце общей настройки тактирования
SysTick_Config(SYSTICKPERIOD);

//Обработчик прерывания системного таймера
void SysTick_Handler(void)
{
    if (SysCounter != 0x00)
      {
	SysCounter--;
      }
}

//Функции временной задержки
void _delay_ticks(uint_fast32_t nTime)	//задержка в тиках таймера
{
   SysCounter = nTime;
  while(SysCounter!= 0) continue;
}
void _delay_ms(uint_fast32_t nTime)		//задержка в мс
{
   SysCounter = nTime*TICKS_ON_MS;
  while(SysCounter!= 0) continue;
}



опытным путем было установлено, что при срабатывании таймера чаще, чем раз в 10мкс, начинаются неточности (при том же самом мигании светодиодом временной интервал на ноге не выдерживается точно), а это бывает критично, поэтому остановился на таком варианте

прошу прощения, если нафлудил, просто я тоже пока раскуриваю многие фишки ARMов, и показалось разумным что пусть оно в одной куче будет. А на форуме действительно много чего нету из подобных простых вещей.
0
Реализации задержек STM32

Пользуюсь этим методом:
// функция задержки в миллисекундах
 
void _delay_ms (uint16_t delay)
{
    TIM6->PSC = F_TIMER/1000-1;             // установка предделителя таймера (период 1 ms)
    TIM6->ARR = delay;                      // количество периодов
    TIM6->EGR = TIM_EGR_UG;                 // генерируем событие обновления
    TIM6->CR1 = TIM_CR1_CEN|TIM_CR1_OPM;    // включаем режим одного импульса
    while ((TIM6->CR1 & TIM_CR1_CEN)!=0);   // ждем пока тикает
}

Проще не придумать, так зачем усложнять? Правда применял на F1xx и F4xx, но думаю и на нулевой серии тоже пойдет.
0
Так даже лучше )
0
400 реально мэджик нумбер подобраный осцилографом. )) Как оно рассчитывается в теории понятно, но на практике что-то не очень. Вообщем это дает нам прерывание таймера каждые 100 микросекунд.
может потому что никто нигде не учитывается частота тактирования?
0
  • avatar
  • xar
  • 15 февраля 2016, 08:26
при старте контроллер тактируется от HSI 8МГц, потом таймером делится на (TIM_Prescaler + 1) и на (TIM_Period + 1), получается частота прерываний 8МГц/(401*2) = 9975Гц, период 100.25 мкс
0
Ну ты учел, и понял что магии никакой в этом нет. А еще наверняка понял что сделав делитель 399 получил бы именно 100мкс. Автор же фигней страдает.
0
естественно) а про автора я бы сказал что он еще учится, там много информации, сходу не разберешься
0
Позвольте спросить, зачем Вы здесь это все постите? Вы таким образом набираете опыт или переписываете сюда SPL (Cube) заготовки со вставками изоленты и палок? Прошу принять это как вопрос, а не попытку оскорбить чье-то достоинство.
0
Для получения ответа на ваш вопрос перейдите к первому комментарию.
0
Тогда встречный вопрос: чем форум хуже? Если каждый из нас начнет задавать вопросы тут, то ресурс превратится в форум=)
0
Если есть вопросы по контенту моего блога, то обращайтесь к администрации.
0
а давай я насру в подъезде под дверь. если есть вопросы — обращайся в администрацию
+2
Не понял аналогии. Иди срать в другое место.
0
Ну собственно то же самое и хотел сказать. Опубликовал — готовься к коментариям
0
Ну я не против комментариев. Если по делу, а не потому что занудам и прафисианалам с пустыми блогами что-то опять не понравилось.
0
А форум вообще читает кто-нибудь? )
0
Точно не Вы. Не в обиду=) Мир, Мир, Мир!
0
Писал туда свои глупые вопросы — в блоге как-то больше ответов получаешь )
0
ох как вот это мне не нравится… просто ужас… Как в старом анекдоте: «Установи WIN2000! Тормозни крутейший Pentium III»
+3
Что именно не нравится?
0
бездарная трата ресурсов процессора
0
Во-первых, задержка все равно идет ожиданием (while, for...), во-вторых, число 400 можно посчитать, только брать не частоту кварца, а частоту шины, на которой сидит таймер. В том же F407 таймеры сидят на APB1 и APB2. У первой 42 МГц, у второй 84 МГц максимум. Конкретное значение можно подсмотреть в том же CubeMX при расчете значений RCC.
Правильнее будет делать задержки на SysTick, тем более там работа вообще элементарная. Там же можно сделать и программные таймеры хоть сколько душе угодно, которые будут вызывать колбэк при достижении нужного времени.
0
  • avatar
  • emdc
  • 15 февраля 2016, 20:08
я делаю задержки вот так:

constexpr uint32_t operator "" _ns(unsigned long long t) {
	return t;
}

constexpr uint32_t operator "" _us(unsigned long long t) {
	return t * 1000;
}

constexpr uint32_t operator "" _ms(unsigned long long t) {
	return t * 1000000;
}

class systick {
public:
	static uint32_t systick_ctrl;
	static constexpr uint32_t ns(uint32_t ns, const uint32_t coreclk){
		return (uint32_t)((double)coreclk * (double)ns / 1e9);
	}
	static int wait_mark(uint32_t tick) {
		__disable_irq();
		uint32_t v, start, end;
		start = SysTick->VAL;
		if(start < (tick + 30)) return -1;
		end = start - tick + 12;
		v = start;
		while(v > end){
			v = SysTick->VAL;
		}
		__enable_irq();
		return -0;
	}
	static void wait_until(uint32_t systick_val) {
		uint32_t v;
		v = SysTick->VAL;
		while(v > systick_val){
			v = SysTick->VAL;
		}
	}
	static void wait(uint32_t tick)
	__attribute__ ((noinline))
	__attribute__ ((__section__(".data")))
	{
		SysTick->LOAD = tick;
		SysTick->VAL = 0;
		SysTick->CTRL = SysTick_CTRL_ENABLE_Msk | SysTick_CTRL_CLKSOURCE_Msk;
		while(!(SysTick->CTRL & SysTick_CTRL_COUNTFLAG_Msk));
	}

	static inline uint32_t get() {
		return SysTick->VAL;
	}
	static void begin() {
		systick_ctrl = SysTick->CTRL;
		SysTick->LOAD = 0xFFFFFF;
		SysTick->VAL = 0;
		SysTick->CTRL = SysTick_CTRL_ENABLE_Msk | SysTick_CTRL_CLKSOURCE_Msk;
	}
	static inline void end() {
		SysTick->CTRL = systick_ctrl;
	}
};
0
__disable_irq();
...
 if(start < (tick + 30)) return -1;
...


Наверное нужно обратно разрешить прерывания в случае выхода из метода по ошибке?
0
точно! Просто пока не нарывался :)
wait_mark
wait_until — для задержек внутри потока. сразу после переключения контекста systick достаточно большой, пожтому ошибки и не было.
особенно приколно wait_mark — вы ждёте отсчётов типа 5000 4900 4800 4500 итд, тем самым ваши действия пляшут слева около желаемых временных отметок и общаяя временая диаграмма не растягивается.
просто wait — работает в связке с begin end, которые заправляют счётчик максимально, чтоб отследить очень длинные интервалы.
0
Я последнее время использую протопотоки. Очень удобно.
0
А то же, но на C, а не C++?
0
Это для C++ а как будет выглядеть задержка без зависона в ппроцедуре для обычного C?
0
что Вы имеете ввиду? Чем эта задержка отлично от аналогичной в С? Про какой «зависон» речь?
0
классы методы и т.д. Или я что то не понял?! Зависон — это когда while ждет выполнения условия и программа просто висит в условии, а не выполняются другие куски программы.
0
=) Тут проблема глубже… Писать на С++ не означает того, что у вас появляется многопоточность(многозадачность) шедулер и прочее, а язык за Вас делает практически все. И тут (С) и там (в С++) Ваши «зависоны» никуда не деваются.
0
Только зарегистрированные и авторизованные пользователи могут оставлять комментарии.