реклама на сайте
подробности

 
 
2 страниц V   1 2 >  
Reply to this topicStart new topic
> General Purpose Timer, Input Capture
MarYuriy
сообщение Sep 19 2012, 16:43
Сообщение #1


Участник
*

Группа: Участник
Сообщений: 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
Go to the top of the page
 
+Quote Post
MarYuriy
сообщение Sep 21 2012, 18:58
Сообщение #2


Участник
*

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



Сформулировал вопрос конкретно по таймеру.

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

Откуда берутся эти 2103 тактов?
Go to the top of the page
 
+Quote Post
toweroff
сообщение Sep 21 2012, 20:21
Сообщение #3


Гуру
******

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



А Вы в прерывании таймера еще что-то отправлять в USART пытаетесь? blink.gif
Go to the top of the page
 
+Quote Post
MarYuriy
сообщение Sep 22 2012, 07:38
Сообщение #4


Участник
*

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



Цитата(toweroff @ Sep 21 2012, 23:21) *
А Вы в прерывании таймера еще что-то отправлять в USART пытаетесь? blink.gif


Да, но это для отладки.
Go to the top of the page
 
+Quote Post
toweroff
сообщение Sep 22 2012, 08:16
Сообщение #5


Гуру
******

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



а вот еще момент с вычислением разницы. Есть потенциальная возможность глюков. Я бы как-то так написал:
Код
if (count1>count2)
{
    razn = count1 - count2;
}
else
{
    razn = count2 - count1;
}



Цитата(MarYuriy @ Sep 22 2012, 11:38) *
Да, но это для отладки.

а уверены, что именно это не приводит к непонятным глюкам?
Go to the top of the page
 
+Quote Post
MarYuriy
сообщение Sep 23 2012, 10:27
Сообщение #6


Участник
*

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



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




а уверены, что именно это не приводит к непонятным глюкам?


Не уверен. Буду ещё разбираться.. Напишу, как что-нибудь получится.
Go to the top of the page
 
+Quote Post
MarYuriy
сообщение Sep 25 2012, 12:45
Сообщение #7


Участник
*

Группа: Участник
Сообщений: 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тка флага прерывания
}
}
Go to the top of the page
 
+Quote Post
AHTOXA
сообщение Sep 25 2012, 17:30
Сообщение #8


фанат дивана
******

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



Цитата(MarYuriy @ Sep 25 2012, 18:45) *
Вопрос всё тот же: откуда берутся эти постоянные 2103 тактов?

2103/35000000 = 0,000060086. Видимо, это погрешность вашего кварца, помноженная на PLL.


--------------------
Если бы я знал, что такое электричество...
Go to the top of the page
 
+Quote Post
MarYuriy
сообщение Sep 26 2012, 09:50
Сообщение #9


Участник
*

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



Цитата(AHTOXA @ Sep 25 2012, 20:30) *
2103/35000000 = 0,000060086. Видимо, это погрешность вашего кварца, помноженная на PLL.


Если бы это была погрешность, то она распространилась и на пункты 1 и 2 моего предыдущего сообщения. Плюс 10Мгц у меня от рубидиевого стандарта. Поэтому я грешу на код...
Go to the top of the page
 
+Quote Post
AHTOXA
сообщение Sep 26 2012, 10:39
Сообщение #10


фанат дивана
******

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



Цитата(MarYuriy @ Sep 26 2012, 15:50) *
Если бы это была погрешность, то она распространилась и на пункты 1 и 2 моего предыдущего сообщения.

Почему? В первом и втором случае вы засекаете секунду, которую формирует GPS. А в третьем - секунду, которая формируется из вашего кварца.
Сформируйте не секунду, а полсекунды, и разница между ней и GPS будет ещё более заметнойsm.gif
Что касаемо кода, то после записи в TIM2->PSC нужно делать
Код
TIM2->EGR = TIM_EGR_UG;

чтобы новое значение PSC вступило в действие.


--------------------
Если бы я знал, что такое электричество...
Go to the top of the page
 
+Quote Post
MarYuriy
сообщение Sep 27 2012, 10:02
Сообщение #11


Участник
*

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



Цитата(AHTOXA @ Sep 26 2012, 13:39) *
Почему? В первом и втором случае вы засекаете секунду, которую формирует GPS. А в третьем - секунду, которая формируется из вашего кварца.

Я использую вместо кварца внешнюю частоту (рубидиевый стандарт) для тактирования МК (значит и таймеры тактируются от этой частоты), поэтому такой погрешности быть не может.
Go to the top of the page
 
+Quote Post
AHTOXA
сообщение Sep 27 2012, 10:41
Сообщение #12


фанат дивана
******

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



В формировании секундного импульса участвует не только рубидиевый стандарт. Возможно, вы ошиблись при настройке PLL? Как вы получили из 10МГц 35МГц? И почему TIM2->PSC = 35000 у вас даёт 2000Гц?


--------------------
Если бы я знал, что такое электричество...
Go to the top of the page
 
+Quote Post
MarYuriy
сообщение Sep 27 2012, 13:52
Сообщение #13


Участник
*

Группа: Участник
Сообщений: 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 готов к работе
}
}

Go to the top of the page
 
+Quote Post
AHTOXA
сообщение Sep 27 2012, 15:25
Сообщение #14


фанат дивана
******

Группа: Свой
Сообщений: 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.


--------------------
Если бы я знал, что такое электричество...
Go to the top of the page
 
+Quote Post
MarYuriy
сообщение Sep 28 2012, 18:25
Сообщение #15


Участник
*

Группа: Участник
Сообщений: 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 он не вернёт (скобок не хватает). А зависать он не должен, он и не зависает sm.gif

Цитата
В конце-концов, выдайте не 1Гц, а 1 КГц, и измерьте осциллоскопом, какая частота получится.


В общем, если что-то получится, то отпишусь.
Go to the top of the page
 
+Quote Post

2 страниц V   1 2 >
Reply to this topicStart new topic
1 чел. читают эту тему (гостей: 1, скрытых пользователей: 0)
Пользователей: 0

 


RSS Текстовая версия Сейчас: 26th June 2025 - 21:37
Рейтинг@Mail.ru


Страница сгенерированна за 0.01523 секунд с 7
ELECTRONIX ©2004-2016