Проблема с программированием таймеров на плате STM32F407VET

Всем доброго времени суток!
По необходимости перешел на макетку китайскую STM32F407VET с общепризнанной STM32F103C8T6. Понадобилась Triple mode ADC1…3.
Работаю на IDE 1.8.19 с ядром Кларка, обновляю вручную по ссылке -

последнее обновление от сентября 2023.
На плате STM32F103C8T6 весь мой код на этом ядре компилится и отлично работает. Для управления периферией использую объекты созданные в библиотеке Кларка.
Вот для примера привожу код работы с таймером, который для разных плат выбираемых в IDE “Generic STM32F103C series” и “Generic STM32F407V series” компилится без ошибок, но на STM32F103 он работает и на нужном пине я вижу меандр, а на STM32F407V тишина.

#define STM32F103C  0   // 0 = STM32F407V
#if (STM32F103C)
  #define pinOut  PA7   // свободный остался после подключение LCD2004
#else
  #define pinOut  PA4   
#endif

void setup()
{
  pinMode(pinOut, OUTPUT); 
  Timer2.setMode(TIMER_CH1, TIMER_OUTPUT_COMPARE);  // устанавливаем режим работы канала 1 на сравнение
  Timer2.pause(); // останавливаем таймер перед настройкой 
  #if (STM32F103C)
    Timer2.setPrescaleFactor(720); // устанавливаем делитель === F = 72 000 000 / 720 = 100 000 Hz
  #else
    Timer2.setPrescaleFactor(840); 
  #endif
  Timer2.setOverflow(1000); // переполнение \ считать в таймере до установленного числа и обнулить === F = 100 000 / 1000 = 100 Hz - Период
  Timer2.setCompare(TIMER_CH1, 200); // сравнение \ считать до числа сравнения и вызвать прерывание канала === F = 100 000 => 10 mkS * 200 = 2 mlS - Ширина импульса
  Timer2.attachInterrupt(TIMER_UPDATE_INTERRUPT, func_UPDATE); // активируем прерывание
  //Timer2.attachInterrupt(TIMER_CH1, func_COMP); // активируем прерывание через указание канала
  Timer2.attachInterrupt(TIMER_CC1_INTERRUPT, func_COMP); // можно также активировать через общую переменную прерывания TIMER_CC1...4
  Timer2.refresh(); // обнулить таймер 
  Timer2.resume(); // запускаем таймер
}

void loop() { }

void func_UPDATE() // обработчик прерывания на переполнение
{
  digitalWrite(pinOut, HIGH);
} 
void func_COMP() // обработчик прерывания на сравненеи
{
  digitalWrite(pinOut, LOW);   // 
} 

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

 #define STM32F103C  0   // 0 = STM32F407V
#if (STM32F103C)
  #define pinOut  PA7   // свободный остался после подключение LCD2004
#else
  #define pinOut  PA4   
#endif
 
void setup() {
  pinMode(pinOut, OUTPUT);   
  timer_attach_interrupt(TIMER2, TIMER_UPDATE_INTERRUPT, led1); // прерывание по переполнению
  timer_attach_interrupt(TIMER2, TIMER_CC1_INTERRUPT, led);     // прерывание по сравнению 1
  #if (STM32F103C)
    RCC_BASE->APB1ENR |= RCC_APB1ENR_TIM2EN;     // TIM2EN > включить тактирование tim_2 \ можно убрать это , Арду сама уже запустила все таймеры
  #else
    RCC->APB1ENR |= RCC_APB1ENR_TIM2EN;     // TIM2EN > включить тактирование tim_2 
  #endif
  TIMER2_BASE->CR1 &= ~TIMER_CR1_CEN;          // TIMx_CR1_CEN > стоп таймер
  #if (STM32F103C)
    TIMER2_BASE->PSC = 720-1;                   // TIMx_PSC > значение пред делителя -1 === F = 72 000 000 / 720 = 100000 Hz
  #else
    TIMER2_BASE->PSC = 840-1;                   // на STM32F407V шина APB1ENR работает на половине системной частоты 168МГц/2=84МГц
                                                  // TIMx_PSC > значение пред делителя -1 === F = 84 000 000 / 840 = 100000 Hz
  #endif
  TIMER2_BASE->ARR = 100-1;                  // TIMx_ARR > считать до установленного числа и обнулить === F = 100000 / 100 = 1 kHz | T = 1 mls
  TIMER2_BASE->CCR1 = 25-1;                  // TIMx_CCR1 > регистр сравнения канала 1 === F = 100000 / 25 = 4 kHz | T = 0.25 mls ширина импульса 
  TIMER2_BASE->DIER |= TIMER_DIER_UIE;         // TIMx_DIER_UIE > разрешить прерывание по переполнению
  TIMER2_BASE->DIER |= TIMER_DIER_CC1IE;       // TIMx_DIER_CC1IE > разрешить прерывание по сравнению 1
  TIMER2_BASE->CR1 |= TIMER_CR1_CEN;           // TIMx_CR1_CEN > старт таймер
}

void loop() {
}

void led1(){
  digitalWrite(pinOut, HIGH); 
  TIMER2_BASE->SR &=~ TIMER_SR_UIF;            // TIMx_SR_UIF > сбрасываем флаг прерывания
}
void led(){  //обработчик прерывания 
  digitalWrite(pinOut, LOW); 
  TIMER2_BASE->SR &=~ TIMER_SR_UIF;            // TIMx_SR_UIF > сбрасываем флаг прерывания
}

Сталкивался кто с такой проблемой управления через объекты на STM32 именно серии STM32F4xx?

Конечно можно переделать весь свой проект на управление через регистры, но МК F407 немного отличается от F103 по регистрам и особенно в ADC и DMA , где у меня основной функционал. Надо будет углубляться в мануал (((

@DIMAX активно изучал таймера STM, но давненько не встречал его постов

Сталкивался с подобным.
Код для STM32F1 и STM32F4 а аддоне Кларка написан разными авторами, и потому между ними могут быть (точнее точно есть) нестыковки. Правда я с таким, чтобы код компилировался, но не работал - не сталкивался. Обычно бывает так, что при переносе с F103 на F4xx код и не собирается, потому что функции и дефайны названы немного по разному. Но и такое, как у вас - тоже может быть.
Добавлю еще, что при использовании динамического выделения памяти на контроллерах F4xx аддон Кларка просто тихо виснет без всяких сообщений об ошибках - может у вас этот случай? На это у меня есть свой форк на гитхабе

Благодарю @b707 за оперативный ответ.

В том и дело, что все работает после прошивки. Вот добавил в Loop() метод запроса значения PrescaleFactor из библиотеки Кларка:

  if ((millis()-newTime)>2700) { // показываем раз в 700 млС
       Serial.print("PrescaleFactor T2 = ");
       Serial.println(Timer2.getPrescaleFactor());

и он корректно возвращает “1” по-умолчанию.
Ну подумал он всегда возвращает “1” )))
Добавил этот метод в скетч с управлением в регистрах

    TIMER2_BASE->PSC = 840-1;                   // на STM32F407V шина APB1ENR работает на половине системной частоты 168МГц/2=84МГц
                                                  // TIMx_PSC > значение пред делителя -1 === F = 84 000 000 / 840 = 100000 Hz
  TIMER2_BASE->ARR = 50-1;                  // TIMx_ARR > считать до установленного числа и обнулить === F = 100000 / 100 = 1 kHz | T = 1 mls
  TIMER2_BASE->CCR1 = 25-1;                  // TIMx_CCR1 > регистр сравнения канала 1 === F = 100000 / 25 = 4 kHz | T = 0.25 mls ширина импульса 
  TIMER2_BASE->DIER |= TIMER_DIER_UIE;         // TIMx_DIER_UIE > разрешить прерывание по переполнению
  TIMER2_BASE->DIER |= TIMER_DIER_CC1IE;       // TIMx_DIER_CC1IE > разрешить прерывание по сравнению 1
  TIMER2_BASE->CR1 |= TIMER_CR1_CEN;           // TIMx_CR1_CEN > старт таймер
}

void loop() {
 if ((millis()-newTime)>700) { // показываем раз в 700 млС
       Serial.print("PrescaleFactor T2 = ");
       Serial.println(Timer2.getPrescaleFactor());
       Serial.print("PrescaleFactor T3 = ");
       Serial.println(Timer3.getPrescaleFactor());
     newTime=millis();
  }

}

И метод вернул корректное значение для Timer2 и Timer3
PrescaleFactor T2 = 840
PrescaleFactor T3 = 1
Методы объектов работают! На считывание регистров точно.
А вот на сохранение в регистре никак (((

static inline void timer_set_prescaler(const timer_dev *dev, uint16 psc) {
    (dev->regs).bas->PSC = psc;
}

Этот код из ядра в либе " timer.h" не может записать значение в регистр PSC.
Сюда мы точно попадаем, проверял. Код подменял в этой функции.

Если никто не поможет здесь на форуме куда копать по этой объектной модели управления периферией, буду изучать плотно мануал по F407.
Хорошо хоть через регистры еще аддон Кларка позволяет управлять )))

Не замечал такого - у меня timer_set_prescaler() работает.
А вот на что точно не стоит рассчитывать - на дефолтные значения в настройках таймеров в платах F4xx. Все надо устанавливать явно, в том числе и прескалер 1.
Кстати, вы помните, что в регистр надо писать значение прескалера на 1 меньше?

Добавлю - кстати я припоминаю, что именно с дефолтным прескалером у меня была проблемка - пока не установишь прескалер явно, таймер не стартует.
“но это не точно” - года два уже прошло как писал такое

В моем случае вопрос не в знаниях по таймерах STM и их функционировании.
Для той задачи, которую я решаю, регистры таймеров я уже изучил и успешно управляю ими (скетч2 в посте1).
Проблема у меня функционирования аддона Кларка в управлении через объектную модель, которая прописана в его либах.

Обьектная модель имела бы смысл, если бы управление через ООП обеспечивало переносимость кода между F1xx и F4xx. А из-за того что в разных ветках аддона использованы разные методы, большого смысла в этом ООП нет.
Я сейчас посмотрел - в моем проекте (который включает код и для F1xx и для F4xx - примерно 70% кода через регистры и всего 30 через ООп методы…

Кстати, до кучи
Вы же сами там выше показали код timer_set_prescaler() . Там внутри ничего нет, кроме той же записи значения в регистр PSC, который Вы используете напрямую. Так как оно может не работать?

Я думаю что ошибка где-то в другом месте

Уменьшение это делается в методе объекта Timer при вызове timer_set_prescaler()

void setPrescaleFactor(uint32 factor) { timer_set_prescaler(this->dev, (uint16)(factor - 1)); }

А кстати, можете показать код, как вы вызываете timer_set_prescaler() из скетча? Не совсем понимаю, что мне указывать в качестве аргумента - this->dev. Понимаю, что это ссылка на экземпляр таймера, но как мне определить в скетче?

В том и дело , что методы по таймерам совпадают в том коде, который я привел в посте 1 для инициации прерывания по переполнению. Что мне и надо было.


Но услышав вас, думаю не факт, что они совпадают например по DMA и ADC ((

Вообще я сразу работаю именно с этими экземплярами, а не с обьектами таймера. В аддоне Кларка они заранее определены для всех таймеров с именами в верхнем регистре - TIMER1, TIMER2 и тд
Так что примерно так:

timer_dev* MAIN_TIMER = TIMER2;
timer_dev* OE_TIMER = TIMER3;
....

uint16 prescaler = timer_get_prescaler(MAIN_TIMER) + 1;
	timer_init(OE_TIMER);
	timer_pause(OE_TIMER);
	timer_set_prescaler(OE_TIMER, prescaler - 1);
	timer_oc_set_mode(OE_TIMER, oe_channel, (timer_oc_mode)this->OE_polarity, 0);
	timer_set_reload(OE_TIMER, TIM_MAX_RELOAD);
	timer_cc_enable(OE_TIMER, oe_channel);
	timer_generate_update(OE_TIMER);
	timer_resume(OE_TIMER);

А в вашем случае наверно надо вызвать через парaметр dev
Как-то так:

timer_set_prescaler(Timer2->dev, prescaler - 1);

если только dev не приватный член класса… этого не помню.

ЗЫЫЫ
Еще вспомнил - для контроллеров F4xx вы обязательно должны вызвать метод init() для таймера перед использованием, автоматически он не вызывается, в отличии от кода для F1xxx

1 лайк

Работал с F407 с аддоном Кларка, но использовал от него только низкоуровневую часть, т.е. описание самих регистров.
И обнаружил (!), что как раз регистры таймера там описаны неправильно - скорее всего, тупо перенесены с F103, а в F407 там есть дополнительные поля по сравнению с F103.
Пришлось самому пилить заголовочные файлы, чтобы привести их в соответствие с дэйташитом.
Правда, было это некоторое время назад, могли уже и поправить. Но я бы сравнил описание регистров таймеров с дэйташитом на F407.

хмм ,а тактирование самой шины где сидит таймер (APB) где включается?

Доброе утро, всем!

Вчера не успел проверить, поздно уже было… Сегодня с утра проверил “Вспоминания” )), вызвал init() и все заработало!!!
У меня была мысль о включении тактирования шины и даже проверил это на STM32F103

  #if (STM32F103C)
    //RCC_BASE->APB1ENR |= RCC_APB1ENR_TIM2EN;     // TIM2EN > включить тактирование tim_2 \ можно убрать это , Арду сама уже запустила все таймеры
  #else
    RCC->APB1ENR |= RCC_APB1ENR_TIM2EN;     // TIM2EN > включить тактирование tim_2 
  #endif

увидел, что на F1xx все работает и без команды включения шины. Подумал, что аддон Кларка уже сам все включил на Ардуино и не стал проверять на F4xx . Даже не думал, что для разных серий одного аддона может быть по разному!
Преогромнейше благодарю @b707 за помощь и правильное направление ))

через регистры так

RCC->APB1ENR |= RCC_APB1ENR_TIM2EN;     // TIM2EN > включить тактирование tim_2

через объекты через метод init()
Но проблема была в том, что аддон Кларка для серии F1xx сам все включает, а для F4xx - нет! Хорошо @b707 подсказал.
А моя ошибка, что я не проверил (

Как я написал, код для 1хх и 4хх писали разные люди. Там и еще такие нестыковки есть, так что будьте готовы. Например, если мне не изменяет память, синтаксис настроек DMA немного разный.

Рад что все запустилось.

@andriano спасибо за участие в моей проблеме!
По случаю хотел бы задать вопрос, как вам удалось использовать библиотеку SPL -Standard Peripherals Library на Ардуино в своем проекте “Разработка процессора звуковых эффектов на STM32F407VET6” и управлять периферией через структуры?

ADC_InitTypeDef ADC_InitStructure;
ADC_StructInit(&ADC_InitStructure);
......
DMA_InitTypeDef DMA_InitStructure;
DMA_StructInit(&DMA_InitStructure);
.....

Вот эти файлы проекта из SPL или уже вами переработанные?

....
#include "STM32F4xx_RCC.H"
#include "STM32F4xx_ADC.H"
#include "STM32F4xx_DMA.H"
#include "STM32F4xx_TIM.H"
....

Естественно, допиливать пришлось.
В основном приведением имен констант к тому варианту, что в аддоне.

Вот СПАСИБО, добрый человек, от всей души. Сколько времени искал этот пост. Добавил timer.init();
И всё заработало на F411
:handshake: