|
General Purpose Timer, Input Capture |
|
|
|
Sep 19 2012, 16:43
|
Участник

Группа: Участник
Сообщений: 59
Регистрация: 20-11-10
Пользователь №: 61 036

|
Здравствуйте! У меня есть, на мой взгляд, интересная задачка (хотя я новичок в МК, может быть, преувеличиваю), над которой я изрядно поломал мозг, но так и не решил. Есть устройство (на основе STM32f107), которое синхронизируется от GPS и тактируется от высокостабильного источника частоты. После того, как прибор засинхронизировался, таймер (TIM4) в режиме Input Capture начинает захватывать 1PPS от GPS в третьем канале и 1PPS от того же GPS в четвёртом канале, дальше из одного значения вычитается другое и получаем... (2103 плюс-минус 3) такта, это, примерно, 29 мкс. Снимая таким образом показания каждую секунду, получаем одно и то же значение. Вопрос возникает сам собой — откуда берутся эти 2103 тактов? Если нужно будет, я выложу весь код. Инициализация таймера: CODE void TIM4_Alarm_Config (void) { RCC->APB1ENR |= RCC_APB1ENR_TIM4EN; // подача тактов на TIM4 от шины тактирования APB1 TIM4->PSC = 1 - 1; // Предварительный делитель: значения счетчика будут инкрементироваться с частотой 35 MГц TIM4->ARR = 65534 - 1; // Максимальное значение, до которого будет считать таймер, равно 65535
// канал 3 (1Гц) TIM4->CCMR2 |= TIM_CCMR2_CC3S_0; // выбор активного входа, канал 3 TIM4_CH3 (PB8) <<------ подать на этот канал 1Гц TIM4->CCER &=~ TIM_CCER_CC3P; // срабатывание по переднему фронту TIM4->CCMR2 &=~ TIM_CCMR2_IC3PSC; // захват каждого импульса // TIM4->CCER |= TIM_CCER_CC3E; // включить захват из счётчика // в EXTI1 TIM4->DIER |= TIM_DIER_CC3IE; // включение прерывания от захвата
// канал 4 (1PPS) TIM4->CCMR2 |= TIM_CCMR2_CC4S_0; // выбор активного входа, канал 4 TIM4_CH4 (PB9) <<------ подать на этот канал 1PPS TIM4->CCER &=~ TIM_CCER_CC4P; // срабатывание по переднему фронту TIM4->CCMR2 &=~ TIM_CCMR2_IC4PSC; // захват каждого импульса // TIM4->CCER |= TIM_CCER_CC4E; // включить захват из счётчика // в EXTI1 TIM4->DIER |= TIM_DIER_CC4IE; // включение прерывания от захвата }
Прерывание от таймера: CODE void TIM4_IRQHandler(void) { if (TIM4->SR&TIM_SR_CC3IF) // 1Гц { TIM4->SR &=~ TIM_SR_CC3IF; // очиcтка флага прерывания count1 = TIM4->CCR3; // считываем значение счётчика в переменную count1, если выставлен флаг прерывания }
if (TIM4->SR&TIM_SR_CC4IF) // 1PPS { TIM4->SR &=~ TIM_SR_CC4IF; // очиcтка флага прерывания count2 = TIM4->CCR4; // считываем значение счётчика в переменную count1, если выставлен флаг прерывания }
if((count1>0)&&(count2>0)) { if(count1>count2) { razn = count1 - count2; } if(count2>count1) { razn = count2 - count1; } count1=0; count2=0; if (razn >(bias+1000)){razn = 65534 - razn;}
// if(bias>razn) // { rash = razn + 100;// - bias; // добавил биас rash - unsigned int // } // if(razn>bias) // { // rash = razn - bias; // }
//while (!(USART2->SR & USART_SR_TXE)) {} // Ждать освобождения буфера. //USART2->DR=rash;
union { unsigned int Mylong2; char buf3[2]; // рассогласование в микросекундах }MyUnion2; MyUnion2.Mylong2 = rash;
while (!(USART2->SR & USART_SR_TXE)) {} // Ждать освобождения буфера. USART2->DR=MyUnion2.buf3[1]; // рассогласование в микросекундах while (!(USART2->SR & USART_SR_TXE)) {} // Ждать освобождения буфера. USART2->DR=MyUnion2.buf3[0];
if (rash<90) {GPIOA->BSRR |= GPIO_BSRR_BS7;} // генерация импульса, который подаётся на GPIOA_PIN6 (PA7), <<----- импульс сигнализации}
if (rash>110) {GPIOA->BSRR |= GPIO_BSRR_BS7;} // генерация импульса, который подаётся на GPIOA_PIN6 (PA7), <<----- импульс сигнализации} } }
Сообщение отредактировал MarYuriy - Sep 19 2012, 16:44
|
|
|
|
|
Sep 21 2012, 18:58
|
Участник

Группа: Участник
Сообщений: 59
Регистрация: 20-11-10
Пользователь №: 61 036

|
Сформулировал вопрос конкретно по таймеру.
Подаю один сигнал параллельно на 2 канала таймера TIM4 (на 2 ноги). Я думаю, что таймер должен защёлкнуть их одновременно, и значения регистров Capture/Compare должны быть одинаковыми. В реальности получается между ними разница в 2103 (плюс минус 3, от включения к включеию) такта шины таймера (35 МГц), которая сохраняется постоянной при приходе этого сигнала, например, раз в секунду.
Откуда берутся эти 2103 тактов?
|
|
|
|
|
Sep 22 2012, 07:38
|
Участник

Группа: Участник
Сообщений: 59
Регистрация: 20-11-10
Пользователь №: 61 036

|
Цитата(toweroff @ Sep 21 2012, 23:21)  А Вы в прерывании таймера еще что-то отправлять в USART пытаетесь?  Да, но это для отладки.
|
|
|
|
|
Sep 22 2012, 08:16
|

Гуру
     
Группа: Свой
Сообщений: 2 957
Регистрация: 19-09-06
Из: Москва
Пользователь №: 20 514

|
а вот еще момент с вычислением разницы. Есть потенциальная возможность глюков. Я бы как-то так написал: Код if (count1>count2) { razn = count1 - count2; } else { razn = count2 - count1; } Цитата(MarYuriy @ Sep 22 2012, 11:38)  Да, но это для отладки. а уверены, что именно это не приводит к непонятным глюкам?
|
|
|
|
|
Sep 23 2012, 10:27
|
Участник

Группа: Участник
Сообщений: 59
Регистрация: 20-11-10
Пользователь №: 61 036

|
Цитата(toweroff @ Sep 22 2012, 11:16)  а вот еще момент с вычислением разницы. Есть потенциальная возможность глюков. Я бы как-то так написал: Код if (count1>count2) { razn = count1 - count2; } else { razn = count2 - count1; } а уверены, что именно это не приводит к непонятным глюкам? Не уверен. Буду ещё разбираться.. Напишу, как что-нибудь получится.
|
|
|
|
|
Sep 25 2012, 12:45
|
Участник

Группа: Участник
Сообщений: 59
Регистрация: 20-11-10
Пользователь №: 61 036

|
Опять всё напутал. Извиняюсь! В общем теперь задача точно описывается так (всё проверил несколько раз!): 1. Если на оба выхода таймера в режиме Input Capture подаётся один и тот же сигнал, то разница между регистрами сравнения каналов = 00 либо 01 (т.е 1 такт при частоте шины тактирования таймера 35 Мгц). 2. Если на выходы таймера в режиме Input Capture подаются сигналы (1PPS) с разных GPS-ов, то разница между регистрами сравнения каналов варьируется и ползает в разные стороны в пределах от 00 до 10. 3. Если же я из 35МГц (которые получаюся из 10МГц) генерирую 1Гц и в режиме Capture/Campare сравниваю с любым из PPS-ов (т. е. от любого из GPS-ов ), то разница между регистрами сравнения = 2103 (плюс-минус 3). Вопрос всё тот же: откуда берутся эти постоянные 2103 тактов? Инициализация и прерывание для таймера, генерирующего 1Гц: CODE //******************************************************************************** * // Функция TIM2_Divider_Config: Делитель на таймере //******************************************************************************** * void TIM2_Divider_Config (void) { RCC->APB1ENR |= RCC_APB1ENR_TIM2EN; // подача тактов на TIM2 от шины тактирования APB1
TIM2->PSC = 35000 - 1; // предделитель до частоты 2000 Гц TIM2->ARR = 2000 - 1; // выход в прерывание каждую секунду TIM2->DIER |= TIM_DIER_UIE; // После того как таймер достигнет своего максимального значения генерируется прерываие TIM2->CR1 |= TIM_CR1_ARPE; // сброс по достижении максимального значения // TIM2->CR1 |= TIM_CR1_CEN; // Разрешить работу таймера } //******************************************************************************** * // Функция TIM2_IRQHandler: Обработчик прерываний для TIM2 //******************************************************************************** * void TIM2_IRQHandler(void) { if (TIM2->SR&TIM_SR_UIF) { GPIOE->BSRR |= GPIO_BSRR_BS0; // генерация импульса, который подаётся на (PE0), его надо принять на TIM4_CH3 (PB8) GPIOE->BSRR |= GPIO_BSRR_BR0; TIM2->SR &=~ TIM_SR_UIF; // очиcтка флага прерывания } }
|
|
|
|
|
Sep 26 2012, 09:50
|
Участник

Группа: Участник
Сообщений: 59
Регистрация: 20-11-10
Пользователь №: 61 036

|
Цитата(AHTOXA @ Sep 25 2012, 20:30)  2103/35000000 = 0,000060086. Видимо, это погрешность вашего кварца, помноженная на PLL. Если бы это была погрешность, то она распространилась и на пункты 1 и 2 моего предыдущего сообщения. Плюс 10Мгц у меня от рубидиевого стандарта. Поэтому я грешу на код...
|
|
|
|
|
Sep 26 2012, 10:39
|

фанат дивана
     
Группа: Свой
Сообщений: 3 387
Регистрация: 9-08-07
Из: Уфа
Пользователь №: 29 684

|
Цитата(MarYuriy @ Sep 26 2012, 15:50)  Если бы это была погрешность, то она распространилась и на пункты 1 и 2 моего предыдущего сообщения. Почему? В первом и втором случае вы засекаете секунду, которую формирует GPS. А в третьем - секунду, которая формируется из вашего кварца. Сформируйте не секунду, а полсекунды, и разница между ней и GPS будет ещё более заметной  Что касаемо кода, то после записи в TIM2->PSC нужно делать Код TIM2->EGR = TIM_EGR_UG; чтобы новое значение PSC вступило в действие.
--------------------
Если бы я знал, что такое электричество...
|
|
|
|
|
Sep 27 2012, 10:02
|
Участник

Группа: Участник
Сообщений: 59
Регистрация: 20-11-10
Пользователь №: 61 036

|
Цитата(AHTOXA @ Sep 26 2012, 13:39)  Почему? В первом и втором случае вы засекаете секунду, которую формирует GPS. А в третьем - секунду, которая формируется из вашего кварца. Я использую вместо кварца внешнюю частоту (рубидиевый стандарт) для тактирования МК (значит и таймеры тактируются от этой частоты), поэтому такой погрешности быть не может.
|
|
|
|
|
Sep 27 2012, 13:52
|
Участник

Группа: Участник
Сообщений: 59
Регистрация: 20-11-10
Пользователь №: 61 036

|
Цитата(AHTOXA @ Sep 27 2012, 13:41)  В формировании секундного импульса участвует не только рубидиевый стандарт. По схемам (RM0008 (стр.123) и даташит на stm32f107 (стр.12)) должен участвовать только он, как я понимаю. Цитата(AHTOXA @ Sep 27 2012, 13:41)  Возможно, вы ошиблись при настройке PLL? Как вы получили из 10МГц 35МГц? И почему TIM2->PSC = 35000 у вас даёт 2000Гц? Я как раз задавал этот вопрос на форуме несколько месяцев назад. Если посмотреть на схему тактирования stm32f107 (RM0008 стр.123), то там есть такая строчка "TIM2,3,4,5,6,7 If(APB1 prescaler =1) x1 else x2", т.е у меня частота тактирования таймера TIM2 = 70 МГц. Код инициализации HSEBYP: CODE unsigned char InitHSEBYP(void) { unsigned long int TimeOut = 10000000;
//Запустить HSEBYP RCC->CR |= RCC_CR_HSEBYP; //Включить генератор HSEBYP RCC->CR |= RCC_CR_HSEON; //Включить генератор HSE while((RCC->CR & RCC_CR_HSERDY)==0) //Ожидание готовности HSE if(TimeOut) TimeOut--; if(TimeOut==0) return 1; // Генератор HSE не запустился RCC->CR |= RCC_CR_CSSON; //Разрешить работу системы защиты от сбоя HSE
RCC->CFGR &= ~RCC_CFGR_PLLXTPRE; //Не использовать делитель HSE
//Частота SystemCoreClock выше 24 MHz - разрешить буфер предварительной выборки FLASH FLASH->ACR|= FLASH_ACR_PRFTBE; //Включить буфер предварительной выборки FLASH->ACR&= ~FLASH_ACR_LATENCY; //Очистить FLASH_ACR_LATENCY FLASH->ACR |= FLASH_ACR_LATENCY_2; //Пропускать 2 такта
//Настройка PLL RCC->CFGR |= RCC_CFGR_PLLSRC; //Источником сигнала для PLL выбран HSE RCC->CR &= ~RCC_CR_PLLON; //Отключить генератор PLL RCC->CFGR &= ~RCC_CFGR_PLLMULL; //Очистить PLLMULL RCC->CFGR |= RCC_CFGR_PLLMULL7; //Коефициент умножения = 7 RCC->CR |= RCC_CR_PLLON; //Включить генератор PLL while((RCC->CR & RCC_CR_PLLRDY)==0) {} //Ожидание готовности PLL
//Переключиться на тактирование от PLL RCC->CFGR &= ~RCC_CFGR_SW; //Очистка битов выбора источника тактового сигнала RCC->CFGR |= RCC_CFGR_SW_PLL; //Выбрать источником тактового сигнала PLL while((RCC->CFGR&RCC_CFGR_SWS)!=0x08){}//Ожидание переключения на PLL
// apb1 RCC->CFGR &=~RCC_CFGR_PPRE1; // Очистка битов предделителя "APB1 Prescaler" // RCC->CFGR |= RCC_CFGR_PPRE1_DIV1; // HCLK not divided RCC->CFGR |= RCC_CFGR_PPRE1_DIV2; // HCLK divided by 2
// apb2 RCC->CFGR &= ~RCC_CFGR_PPRE2; // Очистка битов предделителя "APB2 Prescaler" // RCC->CFGR |= RCC_CFGR_PPRE2_DIV1; // HCLK not divided RCC->CFGR |= RCC_CFGR_PPRE2_DIV2; // HCLK divided by 2
return 0; // HSE готов к работе } }
|
|
|
|
|
Sep 27 2012, 15:25
|

фанат дивана
     
Группа: Свой
Сообщений: 3 387
Регистрация: 9-08-07
Из: Уфа
Пользователь №: 29 684

|
Цитата(MarYuriy @ Sep 27 2012, 19:52)  По схемам (RM0008 (стр.123) и даташит на stm32f107 (стр.12)) должен участвовать только он, как я понимаю. Как минимум ещё PLL и таймер. Нуи время входа в прерывание и время на дрыгание ножкой. Цитата(MarYuriy @ Sep 27 2012, 19:52)  "TIM2,3,4,5,6,7 If(APB1 prescaler =1) x1 else x2", т.е у меня частота тактирования таймера TIM2 = 70 МГц. А, тогда понятно. Просто цифра 70 МГц не фигурировала. Что касаемо приведённых настроек PLL, то я не вижу там ни одного упоминания о RCC->CFGR2. У вас же 107? Надо настроить. В конце-концов, выдайте не 1Гц, а 1 КГц, и измерьте осциллоскопом, какая частота получится. ЗЫ. Кстати, код Код while((RCC->CR & RCC_CR_HSERDY)==0) //Ожидание готовности HSE if(TimeOut) TimeOut--; if(TimeOut==0) return 1; // Генератор HSE не запустился никогда не вернёт 1. Вместо этого он наглухо зависнет в ожидании HSERDY.
--------------------
Если бы я знал, что такое электричество...
|
|
|
|
|
Sep 28 2012, 18:25
|
Участник

Группа: Участник
Сообщений: 59
Регистрация: 20-11-10
Пользователь №: 61 036

|
Цитата Как минимум ещё PLL и таймер. Нуи время входа в прерывание и время на дрыгание ножкой. Только времени(тактов) на это уходит слишком много. Кстати, я синхронизирую включение таймера TIM2 с приходом 1PPS в EXTI, это я забыл написать. И тут тоже не должно быть большого расхождения в тактах: CODE void EXTI1_1PPS_Config(void) { AFIO->EXTICR [0] |= AFIO_EXTICR1_EXTI1_PE; // Прерывание на (PE1) EXTI->IMR |= EXTI_IMR_MR1; // Разрешить запрос от EXTI1 EXTI->RTSR |= EXTI_RTSR_TR1; // Прерывание от EXTI1 по переднему фронту // ширина приходящего по EXTI импульса должна быть минимум --> 10 нс(см. datasheet стр 90) } //******************************************************************************** * // Функция EXTI1_IRQHandler: Синхронизация от импульса 1PPS //******************************************************************************** * void EXTI1_IRQHandler (void) { if (EXTI->PR & EXTI_PR_PR1) // Прерывание от (PE1) по приходу 1PPS { TIM2->CR1 |= TIM_CR1_CEN; // Разрешить работу таймера генерации 1Гц
if (TimeGlobalFlag==1) {// Cинхронизация TIM5->CNT = 0; // обнулить счётчик, чтобы его значение после синхронизации равнялось 0x00 TIM5secs=secs; // если время по NMEA получено, присвоить TIM5secs значения secs
TIM3->CNT = 0; // обнулить счётчик, чтобы его значение после синхронизации равнялось 0x00 TIM3microsecs = 0; // если время по NMEA получено, TIM3microsecs = 0 TIM5->CR1 |= TIM_CR1_CEN; // Разрешить работу секундного таймера TIM5 TIM3->CR1 |= TIM_CR1_CEN; // Разрешить работу микроcекундного TIM3 таймера // TIM2->CR1 |= TIM_CR1_CEN; // Разрешить работу таймера генерации 1Гц TIM1->CCER |= TIM_CCER_CC1E; // включить захват из счётчика TimeGlobalFlag=0; // устанавливаем флаг успешного получения времени // flagEXTI=1; flagSynchro=0; // флаг синхронизации - в ПК будут слаться ответы с временем AFIO->EXTICR [0] &=~ AFIO_EXTICR1_EXTI1_PE; // Отключиться от (PE1) ////////
EXTI->IMR &=~ EXTI_IMR_MR1; // Запретить запрос от EXTI1 } EXTI->PR |= EXTI_PR_PR1; // Сбросить флаг } } Цитата Что касаемо приведённых настроек PLL, то я не вижу там ни одного упоминания о RCC->CFGR2. У вас же 107? Надо настроить. Да 107, только, я так понял, регистр RCC->CFGR2 весь для I2C. Код ЗЫ. Кстати, код Код while((RCC->CR & RCC_CR_HSERDY)==0) //Ожидание готовности HSE if(TimeOut) TimeOut--; if(TimeOut==0) return 1; // Генератор HSE не запустился
никогда не вернёт 1. Вместо этого он наглухо зависнет в ожидании HSERDY. Да, 1 он не вернёт (скобок не хватает). А зависать он не должен, он и не зависает  Цитата В конце-концов, выдайте не 1Гц, а 1 КГц, и измерьте осциллоскопом, какая частота получится. В общем, если что-то получится, то отпишусь.
|
|
|
|
1 чел. читают эту тему (гостей: 1, скрытых пользователей: 0)
Пользователей: 0
|
|
|