Время выполнения кода STM32.

Время выполнения кода STM32.

Всё началось с того что, появилась необходимость хотя бы примерно оценить время выполнения участка кода. Оказалось у микроконтроллеров с ядром Cortex-M3 для этого предназначен специальный модуль, который называется Data Watchpoint and Trace Unit, сокращённо DWT.

Чтобы настроить DWT на измерение длительности выполнения кода, необходимо установить 2 бита.

Бит TRCENA в регистре DEMCR, установка единицы в который разрешает использовать DWT.

Бит CYCCNTENA в регистре DWT_CTRL, установка единицы в который запускает счётчик.

Текущее значение счётчика можно считать из 32-битного регистра CYCCNT.

Код, позволяющий оценить время выполнения кода, выглядит следующим образом

#define    DWT_CYCCNT    *(volatile unsigned long *)0xE0001004
#define    DWT_CONTROL   *(volatile unsigned long *)0xE0001000
#define    SCB_DEMCR     *(volatile unsigned long *)0xE000EDFC

SCB_DEMCR |= CoreDebug_DEMCR_TRCENA_Msk;// разрешаем использовать DWT

DWT_CYCCNT = 0;// обнуляем значение

DWT_CONTROL|= DWT_CTRL_CYCCNTENA_Msk; // включаем счётчик

Какой-то код

count_tic = DWT_CYCCNT;//смотрим сколько натикало


count_tiс – количество тактов, в течение которых выполнялся код. Если мы хотим получить время надо разделить это значение на тактовую частоту, в моём случае на 56 000 000.

В принципе на этом можно было бы закончить, но есть желание разобраться соответствует ли в действительности то, что мы считываем из DWT_CYCCNT реальному времени.

Для того чтобы проверить это, давайте настроим таймер, который будет генерировать прерывание раз в секунду, внутри прерывания будем считывать значение DWT_CYCCNT, обнулять его и так по кругу.


#include "stm32f10x.h"

#define    DWT_CYCCNT    *(volatile unsigned long *)0xE0001004
#define    DWT_CONTROL   *(volatile unsigned long *)0xE0001000
#define    SCB_DEMCR     *(volatile unsigned long *)0xE000EDFC
	
uint32_t count_tic = 0;

void TIM3_IRQHandler(void)
{
	count_tic =  DWT_CYCCNT;
	//останавливаем DWT
	DWT_CONTROL &= ~DWT_CTRL_CYCCNTENA_Msk; 
	DWT_CYCCNT  = 0;
	//выключаем таймер
	TIM3->CR1 &= ~TIM_CR1_CEN;
	//обнуляем его значение
	TIM3->CNT = 0;
	//сбрасываем флаг прерывания 2 раза, у меня он иначе он не сбрасывался
	TIM3->SR &= ~TIM_SR_UIF; 
	TIM3->SR &= ~TIM_SR_UIF; 	
}


int main(void)
{	
	//включаем тактирование таймера
	RCC->APB1ENR |= RCC_APB1ENR_TIM3EN;
	//уcтанавливаем предделитель, новая частота таймера 10KHz
        TIM3->PSC  = 5600 - 1; 
	//период 1 секунда
        TIM3->ARR  = 10000 - 1; 
	//счётчик считает вниз
        TIM3->CR1 &= ~TIM_CR1_DIR; 
	//в режиме одного импульса
        TIM3->CR1 |= TIM_CR1_OPM; 
	//разрешаем прерывания
        TIM3->DIER  |= TIM_DIER_UIE; 
	NVIC->ISER[0] = NVIC_ISER_SETENA_29;
	//запускаем таймер
	TIM3->CR1 |= TIM_CR1_CEN;
	
         //включаем DWT
	 SCB_DEMCR |= CoreDebug_DEMCR_TRCENA_Msk;
	 DWT_CYCCNT  = 0;
	 DWT_CONTROL|= DWT_CTRL_CYCCNTENA_Msk; 
	
	while(1)
	{
               //включаем таймер
		TIM3->CR1 |= TIM_CR1_CEN;
		//запускаем DWT
		DWT_CONTROL|= DWT_CTRL_CYCCNTENA_Msk; 
	}
}

Время  выполнения кода STM32.

Установка бита TIM_CR1_OPM включает режим одиночного импульса, в этом режиме после возникновения события счёт прекращается.
Первое значение отличается от остальных, потому как таймер и счётчик надо бы запустить одновременно, следующие отличаются от расчётного значения на несколько тактов, которые необходимы для сохранения значения DWT_CYCCNT в count_tic, то есть данный способ работает. На этом всё, спасибо за внимание.
комментарии
2