Да. Наверное Вещь. Только очень редкий зверёк в нашем зоопарке. Ни разу не встречал живьём человека с ним работающего. Только читал хвалебные отзывы. Писал про те, которые использовал для отладки сам.
Да. Наверное Вещь. Только очень редкий зверёк в нашем зоопарке. Ни разу не встречал живьём человека с ним работающего. Только читал хвалебные отзывы. Писал про те, которые использовал для отладки сам.
я с ним работал ,правда не на постоянной основе ,а только что бы отладить баг который приводит к Hard Fault .a в Atolllic True Studio , callstack не всегда виден в этом случае
Пытаюсь запустить пример TIM/OCToggle из Peripheral library
Плата blue_pill stm32f103c8t6, пишу в IAR 8.30
вот код
/**
******************************************************************************
* @file TIM/OCToggle/main.c
* @author MCD Application Team
* @version V3.5.0
* @date 08-April-2011
* @brief Main program body
******************************************************************************
******************************************************************************
*/
/* Includes ------------------------------------------------------------------*/
#include "stm32f10x.h"
/
/* Private typedef -----------------------------------------------------------*/
/* Private define ------------------------------------------------------------*/
/* Private macro -------------------------------------------------------------*/
/* Private variables ---------------------------------------------------------*/
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
TIM_OCInitTypeDef TIM_OCInitStructure;
__IO uint16_t CCR1_Val = 32768;
__IO uint16_t CCR2_Val = 16384;
__IO uint16_t CCR3_Val = 8192;
__IO uint16_t CCR4_Val = 4096;
uint16_t PrescalerValue = 0;
/* Private function prototypes -----------------------------------------------*/
void RCC_Configuration(void);
void GPIO_Configuration(void);
void NVIC_Configuration(void);
int main(void)
{
/* System Clocks Configuration */
RCC_Configuration();
/* NVIC Configuration */
NVIC_Configuration();
/* GPIO Configuration */
GPIO_Configuration();
/* ---------------------------------------------------------------------------
TIM3 Configuration: Output Compare Toggle Mode:
TIM3CLK = SystemCoreClock / 2,
The objective is to get TIM3 counter clock at 12 MHz:
- Prescaler = (TIM3CLK / TIM3 counter clock) - 1
CC1 update rate = TIM3 counter clock / CCR1_Val = 366.2 Hz
CC2 update rate = TIM3 counter clock / CCR2_Val = 732.4 Hz
CC3 update rate = TIM3 counter clock / CCR3_Val = 1464.8 Hz
CC4 update rate = TIM3 counter clock / CCR4_Val = 2929.6 Hz
----------------------------------------------------------------------------*/
/* Compute the prescaler value */
PrescalerValue = (uint16_t) (SystemCoreClock / 12000000) - 1;
/* Time base configuration */
TIM_TimeBaseStructure.TIM_Period = 65535;
TIM_TimeBaseStructure.TIM_Prescaler = PrescalerValue;
TIM_TimeBaseStructure.TIM_ClockDivision = 0;
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure);
/* Output Compare Toggle Mode configuration: Channel1 */
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_Toggle;
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
TIM_OCInitStructure.TIM_Pulse = CCR1_Val;
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_Low;
TIM_OC1Init(TIM3, &TIM_OCInitStructure);
TIM_OC1PreloadConfig(TIM3, TIM_OCPreload_Disable);
/* Output Compare Toggle Mode configuration: Channel2 */
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
TIM_OCInitStructure.TIM_Pulse = CCR2_Val;
TIM_OC2Init(TIM3, &TIM_OCInitStructure);
TIM_OC2PreloadConfig(TIM3, TIM_OCPreload_Disable);
/* Output Compare Toggle Mode configuration: Channel3 */
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
TIM_OCInitStructure.TIM_Pulse = CCR3_Val;
TIM_OC3Init(TIM3, &TIM_OCInitStructure);
TIM_OC3PreloadConfig(TIM3, TIM_OCPreload_Disable);
/* Output Compare Toggle Mode configuration: Channel4 */
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
TIM_OCInitStructure.TIM_Pulse = CCR4_Val;
TIM_OC4Init(TIM3, &TIM_OCInitStructure);
TIM_OC4PreloadConfig(TIM3, TIM_OCPreload_Disable);
/* TIM enable counter */
TIM_Cmd(TIM3, ENABLE);
/* TIM IT enable */
TIM_ITConfig(TIM3, TIM_IT_CC1 | TIM_IT_CC2 | TIM_IT_CC3 | TIM_IT_CC4, ENABLE);
//TIM_ITConfig(TIM3, TIM_IT_CC1 , ENABLE);
while (1)
{}
}
void RCC_Configuration(void)
{
/* PCLK1 = HCLK/4 */
RCC_PCLK1Config(RCC_HCLK_Div4);
/* TIM3 clock enable */
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);
/* GPIOA clock enable */
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOB|
RCC_APB2Periph_GPIOC | RCC_APB2Periph_AFIO, ENABLE);
}
void GPIO_Configuration(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
#ifdef STM32F10X_CL
/*GPIOB Configuration: TIM3 channel1, 2, 3 and 4 */
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6 | GPIO_Pin_7 | GPIO_Pin_8 | GPIO_Pin_9;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOC, &GPIO_InitStructure);
GPIO_PinRemapConfig(GPIO_FullRemap_TIM3, ENABLE);
#else
/* GPIOA Configuration:TIM3 Channel1, 2, 3 and 4 as alternate function push-pull */
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6 | GPIO_Pin_7;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1;
GPIO_Init(GPIOB, &GPIO_InitStructure);
#endif
}
/**
* @brief Configure the nested vectored interrupt controller.
* @param None
* @retval None
*/
void NVIC_Configuration(void)
{
NVIC_InitTypeDef NVIC_InitStructure;
/* Enable the TIM3 global Interrupt */
NVIC_InitStructure.NVIC_IRQChannel = TIM3_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
}
void TIM3_IRQHandler(void)
{
uint16_t capture = 0;
/* TIM3_CH1 toggling with frequency = 183.1 Hz */
if (TIM_GetITStatus(TIM3, TIM_IT_CC1) != RESET)
{
TIM_ClearITPendingBit(TIM3, TIM_IT_CC1 );
capture = TIM_GetCapture1(TIM3);
TIM_SetCompare1(TIM3, capture + CCR1_Val );
}
/* TIM3_CH2 toggling with frequency = 366.2 Hz */
if (TIM_GetITStatus(TIM3, TIM_IT_CC2) != RESET)
{
TIM_ClearITPendingBit(TIM3, TIM_IT_CC2);
capture = TIM_GetCapture2(TIM3);
TIM_SetCompare2(TIM3, capture + CCR2_Val);
}
/* TIM3_CH3 toggling with frequency = 732.4 Hz */
if (TIM_GetITStatus(TIM3, TIM_IT_CC3) != RESET)
{
TIM_ClearITPendingBit(TIM3, TIM_IT_CC3);
capture = TIM_GetCapture3(TIM3);
//TIM_SetCompare3(TIM3, capture + CCR3_Val);
//TIM_SetCompare3(TIM3, CCR3_Val);
}
/* TIM3_CH4 toggling with frequency = 1464.8 Hz */
if (TIM_GetITStatus(TIM3, TIM_IT_CC4) != RESET)
{
TIM_ClearITPendingBit(TIM3, TIM_IT_CC4);
capture = TIM_GetCapture4(TIM3);
TIM_SetCompare4(TIM3, capture + CCR4_Val);
}
}
#ifdef USE_FULL_ASSERT
/**
* @brief Reports the name of the source file and the source line number
* where the assert_param error has occurred.
* @param file: pointer to the source file name
* @param line: assert_param error line source number
* @retval None
*/
void assert_failed(uint8_t* file, uint32_t line)
{
/* User can add his own implementation to report the file name and line number,
ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */
while (1)
{}
}
#endif
Проблема в том, что на выходе всез четырех каналов получаю меандр с одной и той же частотой - в 2 раза ниже частоты таймера. То есть инвертирование состояния пина происходит только один раз за цикл таймера. То ли не срабатывает прерывание Output Compare, то ли срабатывает, но почему-то значение регистра сравнения не увеличивается. К сожалению, осцил у меня одноканальный, поэтому посмотреть, туглятся ли все каналы одновременно при переполнении общего счетчика таймера или каждый в свое время по сравнению- не могу.
Собрал аналогичный проект в Stm32Cube через Hal - получил ровно такой же результат, выходы каналов меняют состояние один раз за цикл.
Я явно чего-то не понимаю.
Добавка - проблема касается только режима OCToggle, OC Active работает, PWM тоже.
Toggle это переключит туда в одном цикле - сюда в другом. В сумме частота должна получится ровно в 2 раза ниже, что и имеем. И из манула на f103 Figure 83. Output compare mode, toggle on OC1. Смотреть там.
Toggle это переключит туда в одном цикле - сюда в другом. В сумме частота должна получится ровно в 2 раза ниже, что и имеем.
Это все верно, если регистр сравнения остается постоянным - будет одно совпадаение за цикл. Но в этом примере при каждом совпадении задется новое значение CCR, так что событие Output Compare за один цикл таймера происходит несколько раз.
Например, счетчик считает до 65535. В начале ставим CCR1 = 1024, по совпадению увеличиваем еще на 1024. то есть ставим 2048 и так далее
В итоге Toggle будет вызываться за цикл 64 раза и частота должна быть в 32 раза больше частоты таймера. Подробнее см комментарии в строчках 47-56 и код обработчика прерывания TIM3 в строчках 171-207
проблема состоит именно в том, что это "умножение частоты" не работает
На натуре. Осциллограф было лень доставать. Частоту измерял тестером. Она прыгала одинаково на каждой ноге от 27 до 96 Гц. Смотрел в отладчике на ноги. Каждое прерывание меняло уровень ноги на противоположный.
Взял не Вашу программу, а стандартный пример из поставки IAR c:\Program Files (x86)\IAR Systems\Embedded Workbench 7.4\arm\examples\ST\STM32F10x\stm32f10x_stdperiph_lib\Project\STM32F10x_StdPeriph_Examples\TIM\tim.eww запустил - все четыре частоты частоты на месте. Ничего не прыгает. тестер показывает 183 366 732 1464 Гц на ногах А6 А7 В0 В1. Сравнивайте со своим. main и it
Взял не Вашу программу, а стандартный пример из поставки IAR c:\Program Files (x86)\IAR Systems\Embedded Workbench 7.4\arm\examples\ST\STM32F10x\stm32f10x_stdperiph_lib\Project\STM32F10x_StdPeriph_Examples\TIM\tim.eww запустил - все четыре частоты частоты на месте. Ничего не прыгает. тестер показывает 183 366 732 1464 Гц на ногах А6 А7 В0 В1.
понятно, это она и есть, я только со значениями прескалера и CCRx игрался, чтобы убедится, что они хотя бы влияют на выход :) так что у меня частоты уже другие должны быть. Но у меня на всех 4х выходах одна частота.
Думаю, я скорее всего где-то напортачил при создании шаблона проекта для ИАРа... Шаблон из этой же SPL почему-то не собирается, не хватает части файлов - пришлось их собирать по инету.
ладно, попробую написать это же на CMSIS. как Димакс посоветовал
Не вижу разницы между CMSIS SPL HAL и LL. Последние три это просто обёртки над CMSIS написанные разными командами индийцев и соответственно имеют разные аббревиатуры на одни и те же регистры ARM. Выучил все четыре последовательно. Из обёрток самая чистая - SPL. Никогда не было проблем. Потом сделали куб и HAL - попробовали сделать абстракцию повыше. В результате оказалось много лишнего кода и плохой прямой доступ к регистрам. Я столкнулся с тем, что на HAL невозможно тонко настраивать таймеры и пришлось писать часть инициализации на CMSIS. Тогда придумали LL драйвера и как вишенка на торте миксирование HAL и LL . Сейчас все проекты начинаю с куба. Ставлю разной периферии либо HAL либо LL драйвера в зависимости надо мне лезть в тонкости регистров или нет. В результате получается проект с полной инициализацией нужной периферии, всеми файлами и необходимыми библиотеками, который компилируется сразу. Дописывать приходится только запуск модулей и логику программы. На CMSIS меня всегда напрягало высчитывать циферки при инициализации ног. Ни разу не встречал, что бы МК работал не так, как описано в мануале. Поэтому играться уже не тянет. Последние игры были с запуском АЦП от четвёртого таймера. Фишка была в том, что на SPL работало, а на HAL и LL нет. Даже когда в конце концов сумел добиться на LL такого же состояния регистров как на SPL всё равно АЦП отказывалось запускаться. Пришлось менять последовательность применения LL команд - помогло. Встречал в сети сообщения, что шаблон инициализации RTC на stm32f4 на LL драйверах не запускает RTC. На HAL всё работает. Это всё я к тому, что в CMSIS все регистры имеют имена как в мануале на процессор. Берем мануал. Открываем описание последовательности команд для инициализации периферии - всегда есть точное описание что и в какой последовательности делать. Там же написаны имена регистров и их побитное описание. Собираем команды. Но на HAL будет быстрее в несколько раз конечный результат.
Это всё не относится к программированию в аддонах ардуины. Аддоны делают инициализацию сами. Поэтому чуть чуть подправить то, что нужно себе с помощью CMSIS самый простой и быстрый способ. Здесь я с Димой согласен полностью. Больше ничего не надо.
Хорошо расписали, плюсую.
Только CMSYS слегка "режет слух", правильно CMSIS (Cortex Microcontroller Software Interface Standard).
Можно ещё вспомнить MicroXplorer (предшественник Куба), который генерил инициализацию для SPL.
b707, всё таки во мне взыграло любопытство, и "перевёл" эту тарабарщину на человеческий cmsis/ардуино. Написал на 1-ом таймере, по аналогии можно на любой переделать. Таки да, на выходах 4 разных частоты, вроде особо не джиттерит даже..
/* пример для F103C8 генерации одним таймером нескольких частот за счёт перепрограммирования
* регистров сранения в прерываниях. Выходы PA8,9,10,11 Частоты с данными параметрами
* 549,36Hz 1098Hz 2197 Hz 4394Hz компилить в ардуине (аддон от STM) без UART и без USB
*/
uint16_t CCR1_Val = 32768;
uint16_t CCR2_Val = 16384;
uint16_t CCR3_Val = 8192;
uint16_t CCR4_Val = 4096;
void setup() {
HardwareTimer *MyTim = new HardwareTimer(TIM1);
RCC->APB2ENR|=(1<<11)|(1<<2); //tim1, gpioa clock enable
GPIOA->CRH&=~0xFFFF;
GPIOA->CRH|=0xBBBB; //PA8,9,10,11 output altfunc
TIM1->CCER=(1<<12)|(1<<8)|(1<<4)|(1<<0);//pin enable
TIM1->CCMR1=(3<<12)|(3<<4);//toogle mode
TIM1->CCMR2=(3<<12)|(3<<4);//toogle mode
TIM1->PSC=1;
TIM1->ARR=65535;
TIM1->CCR1=CCR1_Val;
TIM1->CCR2=CCR2_Val;
TIM1->CCR3=CCR3_Val;
TIM1->CCR4=CCR4_Val;
TIM1->BDTR=1<<14;//output enable
TIM1->CR1=1;
MyTim->attachInterrupt(1,tim1_ch1);
MyTim->attachInterrupt(2,tim1_ch2);
MyTim->attachInterrupt(3,tim1_ch3);
MyTim->attachInterrupt(4,tim1_ch4);
}
void tim1_ch1() {TIM1->CCR1+=CCR1_Val;}
void tim1_ch2() {TIM1->CCR2+=CCR2_Val;}
void tim1_ch3() {TIM1->CCR3+=CCR3_Val;}
void tim1_ch4() {TIM1->CCR4+=CCR4_Val;}
void loop(){}
b707, всё таки во мне взыграло любопытство, и "перевёл" эту тарабарщину на человеческий cmsis/ардуино. Написал на 1-ом таймере, по аналогии можно на любой переделать.
Спасибо, хотя это немного неудачная аналогия, потому что, насколько я понял, у ТИМ1 отдельные векторы прерывания на каждый канал, а у ТИМ3 - одно прерывание на все.
В моем случае именно это сыграло роль - при самостоятельном написании через HAL и Куб я неверно определил источник прерывания при входе в TIM3_IRQHandler, поэтому прерывание по совпадению не работало. Когда нашел ошибку - все запустилось.
Но это при сборке через СТМ-Куб
А вот приведенный выше пример из SPL так и не идет. Хотел посмотеть в отладчике, почему так происходит - но IAR не дает поставить брекпойнт внутри обработчика IRQ. Наверно это правильно, прерывание же не совсем код. Не подскажете, как посмотреть регистры таймера в момент вызова прерывания?
Как это не даёт точку останова в прерывании? Обязательное условие - программа должна быть скомпилирована без ошибок и прерывание должно быть в объектном коде. Если какая то часть программы не вызывается то точку туда поставить нельзя. Число точек ограничено. Нельзя поставить больше какого то числа точек (у меня 5). Вот пример. Точка стоит. Я прерывания ловлю. Проблем нет.
у ТИМ1 отдельные векторы прерывания на каждый канал, а у ТИМ3 - одно прерывание на все.
Тут есть нюас -ардуина не даёт сесть на голый вектор. Поэтому я вынужденно использовал её функцию обработки прерывания. А функция сама разбирается с каналами, и сама программирует регистр DIER. Т.е. при переходе на другой таймер ничего хитрого менять не надо.
b707 пишет:
при самостоятельном написании через HAL и Куб я неверно определил источник прерывания при входе в TIM3_IRQHandler, поэтому прерывание по совпадению не работало. Когда нашел ошибку - все запустилось.
Вот поэтому я пишу в CMSIS, когда вся программа 10 строк а не на 200 -ошибку найти легче :)
Как это не даёт точку останова в прерывании? Обязательное условие - программа должна быть скомпилирована без ошибок и прерывание должно быть в объектном коде.
простите, неверно выразился. Поставить точку дает, но при запуске появляется сообщение "Некоторые брекпойнты будут удалены из отладки" (вольный перевод, точной формулировки сейчас посмотреть не могу) И точка пропадает.
Программа скомпилирована без ошибок и даже без предупреждений...
Как это не даёт точку останова в прерывании? Обязательное условие - программа должна быть скомпилирована без ошибок и прерывание должно быть в объектном коде.
простите, неверно выразился. Поставить точку дает, но при запуске появляется сообщение "Некоторые брекпойнты будут удалены из отладки" (вольный перевод, точной формулировки сейчас посмотреть не могу) И точка пропадает.
Программа скомпилирована без ошибок и даже без предупреждений...
незнаю насколько "умны" разные среды разработки , но каждый раз когда писалась такое ("некоторыe брейк поинты будут удалены") ,то код куда была поставлена точка остановки , десйтвительно не исполнялся
незнаю насколько "умны" разные среды разработки , но каждый раз когда писалась такое ("некоторыe брейк поинты будут удалены") ,то код куда была поставлена точка остановки , десйтвительно не исполнялся
а это идея, которая обьяснила бы все...
А из-за чего прерывание может не исполнятся, так что даже компилятор это видит? - единственное. что приходит в голову - имя обработчика не прописано в хидере или прописано неверно... Надо будет покопаться внимательно.
b707, всё таки во мне взыграло любопытство, и "перевёл" эту тарабарщину на человеческий cmsis/ардуино. Написал на 1-ом таймере, по аналогии можно на любой переделать. Таки да, на выходах 4 разных частоты, вроде особо не джиттерит даже..
/* пример для F103C8 генерации одним таймером нескольких частот за счёт перепрограммирования
* регистров сранения в прерываниях. Выходы PA8,9,10,11 Частоты с данными параметрами
* 549,36Hz 1098Hz 2197 Hz 4394Hz компилить в ардуине (аддон от STM) без UART и без USB
*/
uint16_t CCR1_Val = 32768;
uint16_t CCR2_Val = 16384;
uint16_t CCR3_Val = 8192;
uint16_t CCR4_Val = 4096;
void setup() {
HardwareTimer *MyTim = new HardwareTimer(TIM1);
RCC->APB2ENR|=(1<<11)|(1<<2); //tim1, gpioa clock enable
GPIOA->CRH&=~0xFFFF;
GPIOA->CRH|=0xBBBB; //PA8,9,10,11 output altfunc
TIM1->CCER=(1<<12)|(1<<8)|(1<<4)|(1<<0);//pin enable
TIM1->CCMR1=(3<<12)|(3<<4);//toogle mode
TIM1->CCMR2=(3<<12)|(3<<4);//toogle mode
TIM1->PSC=1;
TIM1->ARR=65535;
TIM1->CCR1=CCR1_Val;
TIM1->CCR2=CCR2_Val;
TIM1->CCR3=CCR3_Val;
TIM1->CCR4=CCR4_Val;
TIM1->BDTR=1<<14;//output enable
TIM1->CR1=1;
MyTim->attachInterrupt(1,tim1_ch1);
MyTim->attachInterrupt(2,tim1_ch2);
MyTim->attachInterrupt(3,tim1_ch3);
MyTim->attachInterrupt(4,tim1_ch4);
}
void tim1_ch1() {TIM1->CCR1+=CCR1_Val;}
void tim1_ch2() {TIM1->CCR2+=CCR2_Val;}
void tim1_ch3() {TIM1->CCR3+=CCR3_Val;}
void tim1_ch4() {TIM1->CCR4+=CCR4_Val;}
void loop(){}
dimax,скажите , почему вы не используете полную CMSIS овскую нотацию ?
вместо
TIM1->CCMR1=(3<<12)|(3<<4);//toogle mode
TIM1->CCMR1 = ((TIM_CCMR1_OC2M_0 | TIM_CCMR1_OC2M_1) << TIM_CCMR1_OC2M) | ( (TIM_CCMR1_OC1M_0 | TIM_CCMR1_OC1M_1 ) << TIM_CCMR1_OC1M) ; // хоть и длиней ,но более читабельней
dimax,скажите , почему вы не используете полную CMSIS овскую нотацию ?
2 причины. (1) люблю краткость :) (2) все дефайны всё равно не запомнить, а куда-то подглядывать -только отвлекать мозг лишней нагрузкой.
mixail844 пишет:
вместо
TIM1->CCMR1=(3<<12)|(3<<4);//toogle mode
TIM1->CCMR1 = ((TIM_CCMR1_OC2M_0 | TIM_CCMR1_OC2M_1) << TIM_CCMR1_OC2M) | ( (TIM_CCMR1_OC1M_0 | TIM_CCMR1_OC1M_1 ) << TIM_CCMR1_OC1M) ; // хоть и длиней ,но более читабельней
Для меня длинные строки не особо-то читабельные, в оперативной памяти мозга не помещаются.. пока дойду взглядом до конца строки -уже не помню что было в начале:(
а исходник ( dmfcomm.cpp ) подключили к проекту? - он ведь сам не подключится и включения .h файла недостаточно
конечно, в списке файлов проекта есть, все хорошо, причем я попробовал в dmfcomm.h добавить тестовую переменную - главный модуль main.c все отлично видит
переименовал из .cpp в .c , переподключил к проекту и все заработало
странно...не понимаю (
ну это что-то очень похожее на то. что было у меня пятью сообщениями выше.
При C++ линковке оптимизатор почему-то может выкинуть процедуру из кода, при С- линковке - нет. Почему. если честно. я пока не понял, хотя сегодня с утра почитал на эту тему заметку на СтэкОверфлоу
Правда. мне казалось. что это не относится к случаям, когда процедура вызывается явно, как у вас в main()
думаю все таки я что то криво сделал изначально в проекте, возможно с настройками путей компилияции откуда брать исходники, постараюсь сделать все с нуля, если повториться вопрос - обязательно напишу.
В ИАР лучший отладчик.
Лучший дебаггер это Ozone. Ни кайло ни тем более яр и рядом не лежали ...
https://www.segger.com/products/development-tools/ozone-j-link-debugger/
Да. Наверное Вещь. Только очень редкий зверёк в нашем зоопарке. Ни разу не встречал живьём человека с ним работающего. Только читал хвалебные отзывы. Писал про те, которые использовал для отладки сам.
я с ним работал ,правда не на постоянной основе ,а только что бы отладить баг который приводит к Hard Fault .a в Atolllic True Studio , callstack не всегда виден в этом случае
Ozone , SES и RTT лучший инструментарий для разработки и отладки.
Уж не знаю в чем дело, но сегодня до 21 МГц измеряет))
а можно тоже спросить?
Пытаюсь запустить пример TIM/OCToggle из Peripheral library
Плата blue_pill stm32f103c8t6, пишу в IAR 8.30
вот код
Проблема в том, что на выходе всез четырех каналов получаю меандр с одной и той же частотой - в 2 раза ниже частоты таймера. То есть инвертирование состояния пина происходит только один раз за цикл таймера. То ли не срабатывает прерывание Output Compare, то ли срабатывает, но почему-то значение регистра сравнения не увеличивается. К сожалению, осцил у меня одноканальный, поэтому посмотреть, туглятся ли все каналы одновременно при переполнении общего счетчика таймера или каждый в свое время по сравнению- не могу.
Собрал аналогичный проект в Stm32Cube через Hal - получил ровно такой же результат, выходы каналов меняют состояние один раз за цикл.
Я явно чего-то не понимаю.
Добавка - проблема касается только режима OCToggle, OC Active работает, PWM тоже.
b707, у меня от халовских портянок мозг взрывыется 8-\ В сиэмсис перепиши :)
В сиэмсис перепиши :)
а приведенный код разве не в сиэмсис ? - я их пока с трудом отличаю
b707, дык HAL это . Пример сиэмсис в #829
b707, дык HAL это . Пример сиэмсис в #829
ага, понял.
b707, дык HAL это .
Это не хал . это SPL.
Хотя такое же г....
Много примеров для таймеров есть в сниппетсах для F0 ...
Toggle это переключит туда в одном цикле - сюда в другом. В сумме частота должна получится ровно в 2 раза ниже, что и имеем. И из манула на f103 Figure 83. Output compare mode, toggle on OC1. Смотреть там.
Toggle это переключит туда в одном цикле - сюда в другом. В сумме частота должна получится ровно в 2 раза ниже, что и имеем.
Это все верно, если регистр сравнения остается постоянным - будет одно совпадаение за цикл. Но в этом примере при каждом совпадении задется новое значение CCR, так что событие Output Compare за один цикл таймера происходит несколько раз.
Например, счетчик считает до 65535. В начале ставим CCR1 = 1024, по совпадению увеличиваем еще на 1024. то есть ставим 2048 и так далее
В итоге Toggle будет вызываться за цикл 64 раза и частота должна быть в 32 раза больше частоты таймера. Подробнее см комментарии в строчках 47-56 и код обработчика прерывания TIM3 в строчках 171-207
проблема состоит именно в том, что это "умножение частоты" не работает
b707,
а теперь внимательно прочтите readme.txt что в в папке с проектом.
Какое на "умножение частоты" , да еще "32 раза больше частоты таймера" ?
Ух и буйная же у вас фантазия.
Посмотрел. На каждое прерывание дергает ногой. Туда-сюда.
Посмотрел. На каждое прерывание дергает ногой. Туда-сюда.
в смысле - опробовали на макете? - работает, частоты на каналах получаются разные?
На натуре. Осциллограф было лень доставать. Частоту измерял тестером. Она прыгала одинаково на каждой ноге от 27 до 96 Гц. Смотрел в отладчике на ноги. Каждое прерывание меняло уровень ноги на противоположный.
И все же придется переводить :
Этот пример показывает как сконфигурировать периферию TIM3 ДЛЯ ГЕНЕРАЦИИ 4Х СИГНАЛОВ С 4МЯ РАЗЛИЧНЫМИ ЧАСТОТАМИ.
Вот только у каждого канала - своя фиксированная частота...
Дальше будем спорить?
Или вам до кучи на пальцах объяснить механизм работы таймера и режим compare?
спасибо, хотя ничего и непонятно... Частота должна быть разной на каждой ноге, но не должна "прыгать"
Вот только у каждого канала - своя фиксированная частота...
так я с этим и не спорю, мне это полностью понятно.
Но только у меня на всех каналах - одна и та же частота
Завтра время будет посмотрю.
Однако как быть с этим?
В итоге Toggle будет вызываться за цикл 64 раза и частота должна быть в 32 раза больше частоты таймера.
проблема состоит именно в том, что это "умножение частоты" не работает
Взял не Вашу программу, а стандартный пример из поставки IAR c:\Program Files (x86)\IAR Systems\Embedded Workbench 7.4\arm\examples\ST\STM32F10x\stm32f10x_stdperiph_lib\Project\STM32F10x_StdPeriph_Examples\TIM\tim.eww запустил - все четыре частоты частоты на месте. Ничего не прыгает. тестер показывает 183 366 732 1464 Гц на ногах А6 А7 В0 В1. Сравнивайте со своим. main и it
В итоге Toggle будет вызываться за цикл 64 раза и частота должна быть в 32 раза больше частоты
эти 32 и 64 - исключительно как пример . Реальная разница частот определяется исключительно отношением ARR / CCRx
Взял не Вашу программу, а стандартный пример из поставки IAR c:\Program Files (x86)\IAR Systems\Embedded Workbench 7.4\arm\examples\ST\STM32F10x\stm32f10x_stdperiph_lib\Project\STM32F10x_StdPeriph_Examples\TIM\tim.eww запустил - все четыре частоты частоты на месте. Ничего не прыгает. тестер показывает 183 366 732 1464 Гц на ногах А6 А7 В0 В1.
понятно, это она и есть, я только со значениями прескалера и CCRx игрался, чтобы убедится, что они хотя бы влияют на выход :) так что у меня частоты уже другие должны быть. Но у меня на всех 4х выходах одна частота.
Думаю, я скорее всего где-то напортачил при создании шаблона проекта для ИАРа... Шаблон из этой же SPL почему-то не собирается, не хватает части файлов - пришлось их собирать по инету.
ладно, попробую написать это же на CMSIS. как Димакс посоветовал
Не вижу разницы между CMSIS SPL HAL и LL. Последние три это просто обёртки над CMSIS написанные разными командами индийцев и соответственно имеют разные аббревиатуры на одни и те же регистры ARM. Выучил все четыре последовательно. Из обёрток самая чистая - SPL. Никогда не было проблем. Потом сделали куб и HAL - попробовали сделать абстракцию повыше. В результате оказалось много лишнего кода и плохой прямой доступ к регистрам. Я столкнулся с тем, что на HAL невозможно тонко настраивать таймеры и пришлось писать часть инициализации на CMSIS. Тогда придумали LL драйвера и как вишенка на торте миксирование HAL и LL . Сейчас все проекты начинаю с куба. Ставлю разной периферии либо HAL либо LL драйвера в зависимости надо мне лезть в тонкости регистров или нет. В результате получается проект с полной инициализацией нужной периферии, всеми файлами и необходимыми библиотеками, который компилируется сразу. Дописывать приходится только запуск модулей и логику программы. На CMSIS меня всегда напрягало высчитывать циферки при инициализации ног. Ни разу не встречал, что бы МК работал не так, как описано в мануале. Поэтому играться уже не тянет. Последние игры были с запуском АЦП от четвёртого таймера. Фишка была в том, что на SPL работало, а на HAL и LL нет. Даже когда в конце концов сумел добиться на LL такого же состояния регистров как на SPL всё равно АЦП отказывалось запускаться. Пришлось менять последовательность применения LL команд - помогло. Встречал в сети сообщения, что шаблон инициализации RTC на stm32f4 на LL драйверах не запускает RTC. На HAL всё работает. Это всё я к тому, что в CMSIS все регистры имеют имена как в мануале на процессор. Берем мануал. Открываем описание последовательности команд для инициализации периферии - всегда есть точное описание что и в какой последовательности делать. Там же написаны имена регистров и их побитное описание. Собираем команды. Но на HAL будет быстрее в несколько раз конечный результат.
Это всё не относится к программированию в аддонах ардуины. Аддоны делают инициализацию сами. Поэтому чуть чуть подправить то, что нужно себе с помощью CMSIS самый простой и быстрый способ. Здесь я с Димой согласен полностью. Больше ничего не надо.
P.S. Спасибо. Исправил.
Хорошо расписали, плюсую.
Только CMSYS слегка "режет слух", правильно CMSIS (Cortex Microcontroller Software Interface Standard).
Можно ещё вспомнить MicroXplorer (предшественник Куба), который генерил инициализацию для SPL.
b707, всё таки во мне взыграло любопытство, и "перевёл" эту тарабарщину на человеческий cmsis/ардуино. Написал на 1-ом таймере, по аналогии можно на любой переделать. Таки да, на выходах 4 разных частоты, вроде особо не джиттерит даже..
b707, всё таки во мне взыграло любопытство, и "перевёл" эту тарабарщину на человеческий cmsis/ардуино. Написал на 1-ом таймере, по аналогии можно на любой переделать.
Спасибо, хотя это немного неудачная аналогия, потому что, насколько я понял, у ТИМ1 отдельные векторы прерывания на каждый канал, а у ТИМ3 - одно прерывание на все.
В моем случае именно это сыграло роль - при самостоятельном написании через HAL и Куб я неверно определил источник прерывания при входе в TIM3_IRQHandler, поэтому прерывание по совпадению не работало. Когда нашел ошибку - все запустилось.
Но это при сборке через СТМ-Куб
А вот приведенный выше пример из SPL так и не идет. Хотел посмотеть в отладчике, почему так происходит - но IAR не дает поставить брекпойнт внутри обработчика IRQ. Наверно это правильно, прерывание же не совсем код. Не подскажете, как посмотреть регистры таймера в момент вызова прерывания?
Как это не даёт точку останова в прерывании? Обязательное условие - программа должна быть скомпилирована без ошибок и прерывание должно быть в объектном коде. Если какая то часть программы не вызывается то точку туда поставить нельзя. Число точек ограничено. Нельзя поставить больше какого то числа точек (у меня 5). Вот пример. Точка стоит. Я прерывания ловлю. Проблем нет.
у ТИМ1 отдельные векторы прерывания на каждый канал, а у ТИМ3 - одно прерывание на все.
Тут есть нюас -ардуина не даёт сесть на голый вектор. Поэтому я вынужденно использовал её функцию обработки прерывания. А функция сама разбирается с каналами, и сама программирует регистр DIER. Т.е. при переходе на другой таймер ничего хитрого менять не надо.
при самостоятельном написании через HAL и Куб я неверно определил источник прерывания при входе в TIM3_IRQHandler, поэтому прерывание по совпадению не работало. Когда нашел ошибку - все запустилось.
Вот поэтому я пишу в CMSIS, когда вся программа 10 строк а не на 200 -ошибку найти легче :)
Как это не даёт точку останова в прерывании? Обязательное условие - программа должна быть скомпилирована без ошибок и прерывание должно быть в объектном коде.
простите, неверно выразился. Поставить точку дает, но при запуске появляется сообщение "Некоторые брекпойнты будут удалены из отладки" (вольный перевод, точной формулировки сейчас посмотреть не могу) И точка пропадает.
Программа скомпилирована без ошибок и даже без предупреждений...
Как это не даёт точку останова в прерывании? Обязательное условие - программа должна быть скомпилирована без ошибок и прерывание должно быть в объектном коде.
простите, неверно выразился. Поставить точку дает, но при запуске появляется сообщение "Некоторые брекпойнты будут удалены из отладки" (вольный перевод, точной формулировки сейчас посмотреть не могу) И точка пропадает.
Программа скомпилирована без ошибок и даже без предупреждений...
незнаю насколько "умны" разные среды разработки , но каждый раз когда писалась такое ("некоторыe брейк поинты будут удалены") ,то код куда была поставлена точка остановки , десйтвительно не исполнялся
незнаю насколько "умны" разные среды разработки , но каждый раз когда писалась такое ("некоторыe брейк поинты будут удалены") ,то код куда была поставлена точка остановки , десйтвительно не исполнялся
а это идея, которая обьяснила бы все...
А из-за чего прерывание может не исполнятся, так что даже компилятор это видит? - единственное. что приходит в голову - имя обработчика не прописано в хидере или прописано неверно... Надо будет покопаться внимательно.
b707, всё таки во мне взыграло любопытство, и "перевёл" эту тарабарщину на человеческий cmsis/ардуино. Написал на 1-ом таймере, по аналогии можно на любой переделать. Таки да, на выходах 4 разных частоты, вроде особо не джиттерит даже..
b707, Еще одни грабли :
Если проект был выбран в плюсах то надо было хэндлер прерывания обрамлять в extern "C" {.......}
mixail844.
им лень определения из хэдера скопипастить, а в " родной" не завезли ...
b707, Еще одни грабли :
Если проект был выбран в плюсах то надо было хэндлер прерывания обрамлять в extern "C" {.......}
mixail844.
им лень определения из хэдера скопипастить, а в " родной" не завезли ...
Ага. спасибо, проект был выбран как С++, а я и не обратил внимания. Extern линковку добавил.
А вот что я должен был скопипастить их хидера - это я не понял, зачем мне копипастить, если я весь хидер целиком подключаю в проект
А вот что я должен был скопипастить их хидера - это я не понял, зачем мне копипастить, если я весь хидер целиком подключаю в проект
Это к dimax и к его рукоблудии на аурдунье...
И это, ну ни хидер ни разу , хэддер или хедер , заголовок короче , а хидер - скрывальщик...
2 причины. (1) люблю краткость :) (2) все дефайны всё равно не запомнить, а куда-то подглядывать -только отвлекать мозг лишней нагрузкой.
Для меня длинные строки не особо-то читабельные, в оперативной памяти мозга не помещаются.. пока дойду взглядом до конца строки -уже не помню что было в начале:(
dimax, можно так оформлять...
2 причины. (1) люблю краткость :) (2) все дефайны всё равно не запомнить, а куда-то подглядывать -только отвлекать мозг лишней нагрузкой.
Если знать нотацию можно и ни куда и не подглядывать, главное запомнить:
TIM_CCMR1_OC1M
Домен_Регистр_Бит
начал новый проект в Keil5, решил начать делать вес по науке C, что то ругается на определение функции, ниже три файла
-
-
- ошибка
.\Objects\dmf4stm01.axf: Error: L6218E: Undefined symbol initDmfDevice (referred from main.o).
.\Objects\dmf4stm01.axf: Error: L6218E: Undefined symbol initDmfDevice (referred from main.o).
в файле dmfcomm.h в описании функции уберите extern, оно тут лишнее
.\Objects\dmf4stm01.axf: Error: L6218E: Undefined symbol initDmfDevice (referred from main.o).
в файле dmfcomm.h в описании функции уберите extern, оно тут лишнее
пробовал, убрал еще раз - не помогло (
ошибка та же
ошибка та же
а исходник ( dmfcomm.cpp ) подключили к проекту? - он ведь сам не подключится и включения .h файла недостаточно
ошибка та же
а исходник ( dmfcomm.cpp ) подключили к проекту? - он ведь сам не подключится и включения .h файла недостаточно
конечно, в списке файлов проекта есть, все хорошо, причем я попробовал в dmfcomm.h добавить тестовую переменную - главный модуль main.c все отлично видит
уточняю - .cpp надо подключить, а не .h
Если подключили - тогда последнее что могу посоветовать - сначала Clean, потом Rebuild All или как там в кейле...
Спасибо, починил:
переименовал из .cpp в .c , переподключил к проекту и все заработало
странно...не понимаю (
Спасибо, починил:
переименовал из .cpp в .c , переподключил к проекту и все заработало
странно...не понимаю (
ну это что-то очень похожее на то. что было у меня пятью сообщениями выше.
При C++ линковке оптимизатор почему-то может выкинуть процедуру из кода, при С- линковке - нет. Почему. если честно. я пока не понял, хотя сегодня с утра почитал на эту тему заметку на СтэкОверфлоу
Правда. мне казалось. что это не относится к случаям, когда процедура вызывается явно, как у вас в main()
Может кто-то из старших пояснит...
думаю все таки я что то криво сделал изначально в проекте, возможно с настройками путей компилияции откуда брать исходники, постараюсь сделать все с нуля, если повториться вопрос - обязательно напишу.