Помощь - Поиск - Пользователи - Календарь
Полная версия этой страницы: stm32 + абсолютный энкодер
Форум разработчиков электроники ELECTRONIX.ru > Микроконтроллеры (MCs) > ARM
Alexsandr000
Здравствуйте уважаемые форумчане!

Хотел спросить у вас совета. Есть абсолютный энкодер с 1024 отсчетами угла поворота и stm32 работающий на 72 МГц, данные с энкодера передаются на МК через 10 выводов. Вал энкодера соединен с валом двигателя через шестерню с передаточным числом 1/7. Была написана программа для определения угла поворота и передачи его на ПК через UART. Тут возникло две проблемы:
1. Данные на ПК иногда передаются не корректно;
2. Как преобразовать угол поворота энкодера в угол поворота двигателя.
вот код:
CODE
#include "main.h"
//////////////////////////////////////////////////////////////////////
int main(void){
init_timer();
init_gpio();
init_clock();
init_uart();
for(i = 0; i < 200000; i++);
if(!RCC_CFGR_SWS_HSE){ GPIOA->ODR ^= GPIO_Pin_0; }
Usart2_Send_String("Hello, world!"); //Передаем строку, сообщающую о готовности микроконтроллера к обмену данными
NVIC_SetPriority(TIM3_IRQn, 1);
while(1){
if(State == 1){
NVIC_DisableIRQ(TIM3_IRQn);
State = 0;
D0 = GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_6);
D1 = GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_11);
D2 = GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_10);
D3 = GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_9);
D4 = GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_8);
D5 = GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_15);
D6 = GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_14);
D7 = GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_13);
D8 = GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_12);
D9 = GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_11);
pos = D0 | (D1 << 1) | (D2 << 2) | (D3 << 3) | (D4 << 4) | (D5 << 5) | (D6 << 6) | (D7 << 7) | (D8 << 8) | (D9 << 9);

if(pos <= convert.INT){
while(!(USART2->SR & USART_SR_TC)); //Проверяем установку флага TC - завершения предыдущей передачи
USART_SendData(USART2, convert.CHAR[0]);
for(i = 0; i < 100; i++);
while(!(USART2->SR & USART_SR_TC)); //Проверяем установку флага TC - завершения предыдущей передачи
USART_SendData(USART2, convert.CHAR[1]);
pos = convert.INT;
}
NVIC_EnableIRQ(TIM3_IRQn);
}
for(i = 0; i < 100000; i++);
}
return 0;
}
void init_gpio(void){
GPIO_InitTypeDef gpio_cfgA, gpio_cfgB, gpio_cfgLED;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
GPIO_StructInit(&gpio_cfgA);
GPIO_StructInit(&gpio_cfgB);
GPIO_StructInit(&gpio_cfgLED);
gpio_cfgA.GPIO_Mode = GPIO_Mode_IN_FLOATING; //GPIO_Mode_Out_PP;
gpio_cfgA.GPIO_Pin = GPIO_Pin_12 | GPIO_Pin_11| GPIO_Pin_10 | GPIO_Pin_9 | GPIO_Pin_8 | GPIO_Pin_6;
gpio_cfgA.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &gpio_cfgA);
gpio_cfgB.GPIO_Mode = GPIO_Mode_IN_FLOATING; //GPIO_Mode_Out_PP;
gpio_cfgB.GPIO_Pin = GPIO_Pin_15 | GPIO_Pin_14| GPIO_Pin_13 | GPIO_Pin_12 | GPIO_Pin_11;
gpio_cfgB.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB, &gpio_cfgB);
gpio_cfgLED.GPIO_Mode = GPIO_Mode_Out_PP;
gpio_cfgLED.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1;
gpio_cfgLED.GPIO_Speed = GPIO_Speed_2MHz;
GPIO_Init(GPIOA, &gpio_cfgLED);
}
void init_timer(){
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE); /* Не забываем затактировать таймер */
TIM_TimeBaseInitTypeDef timer_base;
TIM_TimeBaseStructInit(&timer_base);
timer_base.TIM_Prescaler = 720 - 1; /* Делитель учитывается как TIM_Prescaler + 1, поэтому отнимаем 1 */
timer_base.TIM_Period = 1;
TIM_TimeBaseInit(TIM3, &timer_base);
/* Настраиваем захват сигнала: - канал: 1 - счёт: по нарастанию - источник: напрямую со входа - делитель: отключен - фильтр: отключен */
TIM_ICInitTypeDef timer_ic;
timer_ic.TIM_Channel = TIM_Channel_1;
timer_ic.TIM_ICPolarity = TIM_ICPolarity_BothEdge; //TIM_ICPolarity_Rising;
timer_ic.TIM_ICSelection = TIM_ICSelection_DirectTI;
timer_ic.TIM_ICPrescaler = TIM_ICPSC_DIV1;
timer_ic.TIM_ICFilter = 2;
TIM_PWMIConfig(TIM3, &timer_ic); /* Эта функция настроит канал 1 для захвата периода, а канал 2 - для захвата заполнения. */
TIM_SelectInputTrigger(TIM3, TIM_TS_TI1FP1); /* Выбираем источник для триггера: вход 1 (PA6) */
TIM_SelectSlaveMode(TIM3, TIM_SlaveMode_Reset); /* По событию от триггера счётчик будет сбрасываться. */
TIM_SelectMasterSlaveMode(TIM3, TIM_MasterSlaveMode_Enable); /* Включаем события от триггера */
TIM_ITConfig(TIM3, TIM_IT_CC1, ENABLE); /* Разрешаем таймеру генерировать прерывание по захвату */
TIM_Cmd(TIM3, ENABLE); /* Включаем таймер */
NVIC_EnableIRQ(TIM3_IRQn); /* Разрешаем прерывания таймера TIM3 */
}
void TIM3_IRQHandler(){
if (TIM_GetITStatus(TIM3, TIM_IT_CC1) != RESET) {
TIM_ClearITPendingBit(TIM3, TIM_IT_CC1); /* Даём знать, что обработали прерывание */
State = 1;
GPIOA->ODR ^= GPIO_Pin_1;
}
}


Заранее спасибо!!!
_3m
Цитата(Alexsandr000 @ Oct 20 2014, 17:56) *
Хотел спросить у вас совета. Есть абсолютный энкодер с 1024 отсчетами угла поворота и stm32 работающий на 72 МГц, данные с энкодера передаются на МК через 10 выводов.

Все 10 бит с энкодера необходимо считывать одновременно (одной командой чтения) потому что в процессе чтения код может измениться и в таком случае побитовое чтение даст неправильный результат.
net
QUOTE (_3m @ Oct 20 2014, 18:07) *
Все 10 бит с энкодера необходимо считывать одновременно (одной командой чтения) потому что в процессе чтения код может измениться и в таком случае побитовое чтение даст неправильный результат.

и до тех пор пока два раза не станет одинаковыми smile3046.gif
KnightIgor
Цитата(net @ Oct 20 2014, 18:21) *
и до тех пор пока два раза не станет одинаковыми smile3046.gif

Я бы сказал более обще: применить к получаемым данным подходящий алгоритм фильтрации.
А для конкретного примера распиновки порекомендовал бы еще не собирать данные побитово, а прочитать сразу все содержимое портов A и B и лишь потом рассортировать биты.
Alexsandr000
Это я сделал
CODE
#include "main.h"
//////////////////////////////////////////////////////////////////////
int main(void){
init_timer();
init_gpio();
init_clock();
init_uart();
for(i = 0; i < 200000; i++);
if(!RCC_CFGR_SWS_HSE){ GPIOA->ODR ^= GPIO_Pin_0; }
Usart2_Send_String("Hello, world!"); //Передаем строку, сообщающую о готовности микроконтроллера к обмену данными
NVIC_SetPriority(TIM3_IRQn, 1);
while(1){
if(State == 1){
NVIC_DisableIRQ(TIM3_IRQn);
State = 0;
PA = GPIOA_IDR;
PB = GPIOB_IDR;
D0 = (PA & 0b0000000000100000) >> 6;
D1 = (PA & 0b0000010000000000) >> 11;
D2 = (PA & 0b0000001000000000) >> 10;
D3 = (PA & 0b0000000100000000) >> 9;
D4 = (PA & 0b0000000010000000) >> 8;
D5 = (PB & 0b1000000000000000) >> 15;
D6 = (PB & 0b0100000000000000) >> 14;
D7 = (PB & 0b0010000000000000) >> 13;
D8 = (PB & 0b0001000000000000) >> 12;
D9 = (PB & 0b0000100000000000) >> 11;
/*
D0 = GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_6);
D1 = GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_11);
D2 = GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_10);
D3 = GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_9);
D4 = GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_8);
D5 = GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_15);
D6 = GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_14);
D7 = GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_13);
D8 = GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_12);
D9 = GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_11);
*/
pos = D0 | (D1 << 1) | (D2 << 2) | (D3 << 3) | (D4 << 4) | (D5 << 5) | (D6 << 6) | (D7 << 7) | (D8 << 8);

if(pos <= convert.INT){
while(!(USART2->SR & USART_SR_TC)); //Проверяем установку флага TC - завершения предыдущей передачи
USART_SendData(USART2, convert.CHAR[0]);
for(i = 0; i < 100; i++);
while(!(USART2->SR & USART_SR_TC)); //Проверяем установку флага TC - завершения предыдущей передачи
USART_SendData(USART2, convert.CHAR[1]);
pos = convert.INT;
}
NVIC_EnableIRQ(TIM3_IRQn);
}
for(i = 0; i < 100000; i++);
}
return 0;
}
void init_gpio(void){
GPIO_InitTypeDef gpio_cfgA, gpio_cfgB, gpio_cfgLED;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
GPIO_StructInit(&gpio_cfgA);
GPIO_StructInit(&gpio_cfgB);
GPIO_StructInit(&gpio_cfgLED);
gpio_cfgA.GPIO_Mode = GPIO_Mode_IN_FLOATING; //GPIO_Mode_Out_PP;
gpio_cfgA.GPIO_Pin = GPIO_Pin_12 | GPIO_Pin_11| GPIO_Pin_10 | GPIO_Pin_9 | GPIO_Pin_8 | GPIO_Pin_6;
gpio_cfgA.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &gpio_cfgA);
gpio_cfgB.GPIO_Mode = GPIO_Mode_IN_FLOATING; //GPIO_Mode_Out_PP;
gpio_cfgB.GPIO_Pin = GPIO_Pin_15 | GPIO_Pin_14| GPIO_Pin_13 | GPIO_Pin_12 | GPIO_Pin_11;
gpio_cfgB.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB, &gpio_cfgB);
gpio_cfgLED.GPIO_Mode = GPIO_Mode_Out_PP;
gpio_cfgLED.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1;
gpio_cfgLED.GPIO_Speed = GPIO_Speed_2MHz;
GPIO_Init(GPIOA, &gpio_cfgLED);
}
void init_timer(){
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE); /* Не забываем затактировать таймер */
TIM_TimeBaseInitTypeDef timer_base;
TIM_TimeBaseStructInit(&timer_base);
timer_base.TIM_Prescaler = 720 - 1; /* Делитель учитывается как TIM_Prescaler + 1, поэтому отнимаем 1 */
timer_base.TIM_Period = 1;
TIM_TimeBaseInit(TIM3, &timer_base);
/* Настраиваем захват сигнала: - канал: 1 - счёт: по нарастанию - источник: напрямую со входа - делитель: отключен - фильтр: отключен */
TIM_ICInitTypeDef timer_ic;
timer_ic.TIM_Channel = TIM_Channel_1;
timer_ic.TIM_ICPolarity = TIM_ICPolarity_BothEdge; //TIM_ICPolarity_Rising;
timer_ic.TIM_ICSelection = TIM_ICSelection_DirectTI;
timer_ic.TIM_ICPrescaler = TIM_ICPSC_DIV1;
timer_ic.TIM_ICFilter = 2;
TIM_PWMIConfig(TIM3, &timer_ic); /* Эта функция настроит канал 1 для захвата периода, а канал 2 - для захвата заполнения. */
TIM_SelectInputTrigger(TIM3, TIM_TS_TI1FP1); /* Выбираем источник для триггера: вход 1 (PA6) */
TIM_SelectSlaveMode(TIM3, TIM_SlaveMode_Reset); /* По событию от триггера счётчик будет сбрасываться. */
TIM_SelectMasterSlaveMode(TIM3, TIM_MasterSlaveMode_Enable); /* Включаем события от триггера */
TIM_ITConfig(TIM3, TIM_IT_CC1, ENABLE); /* Разрешаем таймеру генерировать прерывание по захвату */
TIM_Cmd(TIM3, ENABLE); /* Включаем таймер */
NVIC_EnableIRQ(TIM3_IRQn); /* Разрешаем прерывания таймера TIM3 */
}
void TIM3_IRQHandler(){
if (TIM_GetITStatus(TIM3, TIM_IT_CC1) != RESET) {
TIM_ClearITPendingBit(TIM3, TIM_IT_CC1); /* Даём знать, что обработали прерывание */
State = 1;
GPIOA->ODR ^= GPIO_Pin_1;
}
}
KnightIgor
Цитата(Alexsandr000 @ Oct 21 2014, 07:14) *
Это я сделал

Помогло как-то?
net
QUOTE (KnightIgor @ Oct 20 2014, 22:30) *
Я бы сказал более обще: применить к получаемым данным подходящий алгоритм фильтрации.
А для конкретного примера распиновки порекомендовал бы еще не собирать данные побитово, а прочитать сразу все содержимое портов A и B и лишь потом рассортировать биты.

я имел ввиду что считывание может попасть на момент переключения - если запись в регистр данных с устройства не стробируется
KnightIgor
Цитата(net @ Oct 21 2014, 09:35) *
я имел ввиду что считывание может попасть на момент переключения - если запись в регистр данных с устройства не стробируется

Вопрос в том, каково соотношение частот переключения и опроса. Можно ограничиться конкретным случаем и решить, что соотношение разительное, и процессор сто раз успеет. Вполне допустимый подход. Можно, однако, подойти обще и применить фильтрацию, которая исключит сбои и в случае, если завтра моторчик шустро раскрутится, а процессор будет напевать MP3 песенку. Мне в этой связи вспоминается статья (кажется на Хабре) об упавшей ракете Ariane: когда стали разбираться, нашли, что самоликвидация произошла из-за старого участка кода управления, который посчитал более выское ускорение модифицированных двигателей ракеты отклонением от нормы.
Alexsandr000
сделал так, но теперь не пойму почему данные с UART бьются
CODE
#include "main.h"
//////////////////////////////////////////////////////////////////////
int main(void){
init_timer();
init_gpio();
init_clock();
init_uart();
for(i = 0; i < 200000; i++);
if(!RCC_CFGR_SWS_HSE){ GPIOA->ODR ^= GPIO_Pin_0; }
Usart2_Send_String("Hello, world!"); //Передаем строку, сообщающую о готовности микроконтроллера к обмену данными
NVIC_SetPriority(TIM3_IRQn, 1);
while(1){
if(State == 2){
NVIC_DisableIRQ(TIM3_IRQn);
State = 0;
pos = pos_1;
if(pos != old_pos){
if(convert.INT >= 7168 || convert.INT <= -7160) {
convert.INT = 0;
while(!(USART2->SR & USART_SR_TC)); //Проверяем установку флага TC - завершения предыдущей передачи
USART_SendData(USART2, convert.CHAR[0]);
for(i = 0; i < 100; i++);
while(!(USART2->SR & USART_SR_TC)); //Проверяем установку флага TC - завершения предыдущей передачи
USART_SendData(USART2, convert.CHAR[1]);
}
else{
if((old_pos - pos) > 0){
convert.INT = convert.INT - pos;
}
else{
convert.INT = convert.INT + pos;
}
while(!(USART2->SR & USART_SR_TC)); //Проверяем установку флага TC - завершения предыдущей передачи
USART_SendData(USART2, convert.CHAR[0]);
for(i = 0; i < 100; i++);
while(!(USART2->SR & USART_SR_TC)); //Проверяем установку флага TC - завершения предыдущей передачи
USART_SendData(USART2, convert.CHAR[1]);
}
old_pos = pos;
}

NVIC_EnableIRQ(TIM3_IRQn);
}
for(i = 0; i < 100000; i++);
}
return 0;
}
void init_gpio(void){
GPIO_InitTypeDef gpio_cfgA, gpio_cfgB, gpio_cfgLED;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
GPIO_StructInit(&gpio_cfgA);
GPIO_StructInit(&gpio_cfgB);
GPIO_StructInit(&gpio_cfgLED);
gpio_cfgA.GPIO_Mode = GPIO_Mode_IN_FLOATING; //GPIO_Mode_Out_PP;
gpio_cfgA.GPIO_Pin = GPIO_Pin_12 | GPIO_Pin_11| GPIO_Pin_10 | GPIO_Pin_9 | GPIO_Pin_8 | GPIO_Pin_6;
gpio_cfgA.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &gpio_cfgA);
gpio_cfgB.GPIO_Mode = GPIO_Mode_IN_FLOATING; //GPIO_Mode_Out_PP;
gpio_cfgB.GPIO_Pin = GPIO_Pin_15 | GPIO_Pin_14| GPIO_Pin_13 | GPIO_Pin_12 | GPIO_Pin_11;
gpio_cfgB.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB, &gpio_cfgB);
gpio_cfgLED.GPIO_Mode = GPIO_Mode_Out_PP;
gpio_cfgLED.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1;
gpio_cfgLED.GPIO_Speed = GPIO_Speed_2MHz;
GPIO_Init(GPIOA, &gpio_cfgLED);
}
void init_timer(){
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE); /* Не забываем затактировать таймер */
TIM_TimeBaseInitTypeDef timer_base;
TIM_TimeBaseStructInit(&timer_base);
timer_base.TIM_Prescaler = 72 - 1; /* Делитель учитывается как TIM_Prescaler + 1, поэтому отнимаем 1 */
timer_base.TIM_Period = 10;
TIM_TimeBaseInit(TIM3, &timer_base);
/* Настраиваем захват сигнала: - канал: 1 - счёт: по нарастанию - источник: напрямую со входа - делитель: отключен - фильтр: отключен */
TIM_ICInitTypeDef timer_ic;
timer_ic.TIM_Channel = TIM_Channel_1;
timer_ic.TIM_ICPolarity = TIM_ICPolarity_BothEdge; //TIM_ICPolarity_Rising;
timer_ic.TIM_ICSelection = TIM_ICSelection_DirectTI;
timer_ic.TIM_ICPrescaler = TIM_ICPSC_DIV1;
timer_ic.TIM_ICFilter = 2;
TIM_PWMIConfig(TIM3, &timer_ic); /* Эта функция настроит канал 1 для захвата периода, а канал 2 - для захвата заполнения. */
TIM_SelectInputTrigger(TIM3, TIM_TS_TI1FP1); /* Выбираем источник для триггера: вход 1 (PA6) */
TIM_SelectSlaveMode(TIM3, TIM_SlaveMode_Reset); /* По событию от триггера счётчик будет сбрасываться. */
TIM_SelectMasterSlaveMode(TIM3, TIM_MasterSlaveMode_Enable); /* Включаем события от триггера */
TIM_ITConfig(TIM3, TIM_IT_CC1, ENABLE); /* Разрешаем таймеру генерировать прерывание по захвату */
TIM_Cmd(TIM3, ENABLE); /* Включаем таймер */
NVIC_EnableIRQ(TIM3_IRQn); /* Разрешаем прерывания таймера TIM3 */
}
void TIM3_IRQHandler(){
if (TIM_GetITStatus(TIM3, TIM_IT_CC1) != RESET) {
TIM_ClearITPendingBit(TIM3, TIM_IT_CC1); /* Даём знать, что обработали прерывание */
if(State == 0){
State = 1;
PA = GPIOA->IDR;
PB = GPIOB->IDR;
D0 = (~PA & 0b0000000000100000) >> 6;
D1 = (~PA & 0b0000010000000000) >> 11;
D2 = (~PA & 0b0000001000000000) >> 10;
D3 = (~PA & 0b0000000100000000) >> 9;
D4 = (~PA & 0b0000000010000000) >> 8;
D5 = (~PB & 0b1000000000000000) >> 15;
D6 = (~PB & 0b0100000000000000) >> 14;
D7 = (~PB & 0b0010000000000000) >> 13;
D8 = (~PB & 0b0001000000000000) >> 12;
D9 = (~PB & 0b0000100000000000) >> 11;
pos_1 = D0 | (D1 << 1) | (D2 << 2) | (D3 << 3) | (D4 << 4) | (D5 << 5) | (D6 << 6) | (D7 << 7) | (D8 << 8) | (D9 << 9);
}
else if(State == 1){
PA2 = GPIOA->IDR;
PB2 = GPIOB->IDR;
D0 = (~PA2 & 0b0000000000100000) >> 6;
D1 = (~PA2 & 0b0000010000000000) >> 11;
D2 = (~PA2 & 0b0000001000000000) >> 10;
D3 = (~PA2 & 0b0000000100000000) >> 9;
D4 = (~PA2 & 0b0000000010000000) >> 8;
D5 = (~PB2 & 0b1000000000000000) >> 15;
D6 = (~PB2 & 0b0100000000000000) >> 14;
D7 = (~PB2 & 0b0010000000000000) >> 13;
D8 = (~PB2 & 0b0001000000000000) >> 12;
D9 = (~PB2 & 0b0000100000000000) >> 11;
pos_2 = D0 | (D1 << 1) | (D2 << 2) | (D3 << 3) | (D4 << 4) | (D5 << 5) | (D6 << 6) | (D7 << 7) | (D8 << 8) | (D9 << 9);
if(pos_2 == pos_1){
State = 2;
}
else{
State = 0;
}
}
GPIOA->ODR ^= GPIO_Pin_1;
}
}
net
QUOTE (KnightIgor @ Oct 21 2014, 13:05) *
Вопрос в том, каково соотношение частот переключения и опроса. Можно ограничиться конкретным случаем и решить, что соотношение разительное, и процессор сто раз успеет. Вполне допустимый подход.

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


Alexsandr000
Цитата(net @ Oct 21 2014, 14:41) *
нееее - так нельзя - потому как будет сбоить один раз в год, но в самымй неподходящий момент

И сбоит, каждые 2-3 периода
AHTOXA
Цитата(Alexsandr000 @ Oct 21 2014, 16:58) *
И сбоит, каждые 2-3 периода

Вы не в разных прерываниях по таймеру читайте два раза, а прямо подряд, в одном прерывании:
Код
uint16_t ReadEncoder()
{
    uint16_t PA, PB;
    PA = ~GPIOA->IDR;
    PB = ~GPIOB->IDR;
    return 0
        | ((PA & 0b0000000000100000) >> (6 - 0))
        | ((PA & 0b0000010000000000) >> (11 - 1))
        | ((PA & 0b0000001000000000) >> (10 - 2))
        | ((PA & 0b0000000100000000) >> (9 - 3))
        | ((PA & 0b0000000010000000) >> (8 - 4))
        | ((PB & 0b1000000000000000) >> (15 - 5))
        | ((PB & 0b0100000000000000) >> (14 - 6))
        | ((PB & 0b0010000000000000) >> (13 - 7))
        | ((PB & 0b0001000000000000) >> (12 - 8))
        | ((PB & 0b0000100000000000) >> (11 - 9));
}


И в прерывании:
Код
void TIM3_IRQHandler()
{
    uint16_t enc1, enc2;
    if (TIM_GetITStatus(TIM3, TIM_IT_CC1) != RESET)
    {
        TIM_ClearITPendingBit(TIM3, TIM_IT_CC1);
        do
        {
            enc1 = ReadEncoder();
            enc2 = ReadEncoder();
        } while (enc1!=enc2);
        // здесь используем правильное значение enc1.
    }
}
KnightIgor
Цитата(net @ Oct 21 2014, 12:41) *
нееее - так нельзя - потому как будет сбоить один раз в год, но в самымй неподходящий момент

Именно тот случай, когда фраза вырывается из контекста. Там дальше была-таки рекомендация применить общий подход с фильтрацией.
А сбой раз в год гарантирован в любом случае: сбой флэш, бросок питания, и т.п.
Alexsandr000
Огромное всем спасибо!!!
Ну сбоить будет в любом случае, это не ПЛИС. Я думаю отправлять данные сразу на UART, а фильтровать на ПК.
Только не совсем пока понятно как отфильтровывать. При вращении периодически сбоит, но я думаю с МК уже ничего не выжмешь. Кстати код:
CODE
#include "main.h"
//////////////////////////////////////////////////////////////////////
int main(void){
init_timer();
init_gpio();
init_clock();
init_uart();
for(i = 0; i < 200000; i++);
if(!RCC_CFGR_SWS_HSE){ GPIOA->ODR ^= GPIO_Pin_0; }
Usart2_Send_String("Hello, world!"); //Передаем строку, сообщающую о готовности микроконтроллера к обмену данными
NVIC_SetPriority(TIM3_IRQn, 1);
while(1){
if(State == 1){
NVIC_DisableIRQ(TIM3_IRQn);
State = 0;
//pos = //pos_1;
if(pos != old_pos){
convert.INT = pos;
while(!(USART2->SR & USART_SR_TC)); //Проверяем установку флага TC - завершения предыдущей передачи
USART_SendData(USART2, convert.CHAR[0]);
for(i = 0; i < 100; i++);
while(!(USART2->SR & USART_SR_TC)); //Проверяем установку флага TC - завершения предыдущей передачи
USART_SendData(USART2, convert.CHAR[1]);
old_pos = pos;
}
NVIC_EnableIRQ(TIM3_IRQn);
}
for(i = 0; i < 100000; i++);
}
return 0;
}
void init_gpio(void){
GPIO_InitTypeDef gpio_cfgA, gpio_cfgB, gpio_cfgLED;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
GPIO_StructInit(&gpio_cfgA);
GPIO_StructInit(&gpio_cfgB);
GPIO_StructInit(&gpio_cfgLED);
gpio_cfgA.GPIO_Mode = GPIO_Mode_IN_FLOATING; //GPIO_Mode_Out_PP;
gpio_cfgA.GPIO_Pin = GPIO_Pin_12 | GPIO_Pin_11| GPIO_Pin_10 | GPIO_Pin_9 | GPIO_Pin_8 | GPIO_Pin_6;
gpio_cfgA.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &gpio_cfgA);
gpio_cfgB.GPIO_Mode = GPIO_Mode_IN_FLOATING; //GPIO_Mode_Out_PP;
gpio_cfgB.GPIO_Pin = GPIO_Pin_15 | GPIO_Pin_14| GPIO_Pin_13 | GPIO_Pin_12 | GPIO_Pin_11;
gpio_cfgB.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB, &gpio_cfgB);
gpio_cfgLED.GPIO_Mode = GPIO_Mode_Out_PP;
gpio_cfgLED.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1;
gpio_cfgLED.GPIO_Speed = GPIO_Speed_2MHz;
GPIO_Init(GPIOA, &gpio_cfgLED);
}
void init_timer(){
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE); /* Не забываем затактировать таймер */
TIM_TimeBaseInitTypeDef timer_base;
TIM_TimeBaseStructInit(&timer_base);
timer_base.TIM_Prescaler = 72 - 1; /* Делитель учитывается как TIM_Prescaler + 1, поэтому отнимаем 1 */
timer_base.TIM_Period = 10;
TIM_TimeBaseInit(TIM3, &timer_base);
/* Настраиваем захват сигнала: - канал: 1 - счёт: по нарастанию - источник: напрямую со входа - делитель: отключен - фильтр: отключен */
TIM_ICInitTypeDef timer_ic;
timer_ic.TIM_Channel = TIM_Channel_1;
timer_ic.TIM_ICPolarity = TIM_ICPolarity_Rising;
timer_ic.TIM_ICSelection = TIM_ICSelection_DirectTI;
timer_ic.TIM_ICPrescaler = TIM_ICPSC_DIV1;
timer_ic.TIM_ICFilter = 20;
TIM_PWMIConfig(TIM3, &timer_ic); /* Эта функция настроит канал 1 для захвата периода, а канал 2 - для захвата заполнения. */
TIM_SelectInputTrigger(TIM3, TIM_TS_TI1FP1); /* Выбираем источник для триггера: вход 1 (PA6) */
TIM_SelectSlaveMode(TIM3, TIM_SlaveMode_Reset); /* По событию от триггера счётчик будет сбрасываться. */
TIM_SelectMasterSlaveMode(TIM3, TIM_MasterSlaveMode_Enable); /* Включаем события от триггера */
TIM_ITConfig(TIM3, TIM_IT_CC1, ENABLE); /* Разрешаем таймеру генерировать прерывание по захвату */
TIM_Cmd(TIM3, ENABLE); /* Включаем таймер */
NVIC_EnableIRQ(TIM3_IRQn); /* Разрешаем прерывания таймера TIM3 */
}
void TIM3_IRQHandler(){
uint16_t enc1, enc2;
if (TIM_GetITStatus(TIM3, TIM_IT_CC1) != RESET) {
TIM_ClearITPendingBit(TIM3, TIM_IT_CC1);
do {
enc1 = ReadEncoder();
enc2 = ReadEncoder();
} while (enc1!=enc2);
// здесь используем правильное значение enc1.
if(State == 0){
State = 1;
pos = enc1;
}
GPIOA->ODR ^= GPIO_Pin_1;
}
}

uint16_t ReadEncoder()
{
uint16_t PA, PB;
PA = ~GPIOA->IDR;
PB = ~GPIOB->IDR;
return 0
| (PA & 0b0000000000100000) >> (6 - 0)
| (PA & 0b0000010000000000) >> (11 - 1)
| (PA & 0b0000001000000000) >> (10 - 2)
| (PA & 0b0000000100000000) >> (9 - 3)
| (PA & 0b0000000010000000) >> (8 - 4)
| (PB & 0b1000000000000000) >> (15 - 5)
| (PB & 0b0100000000000000) >> (14 - 6)
| (PB & 0b0010000000000000) >> (13 - 7)
| (PB & 0b0001000000000000) >> (12 - 8)
| (PB & 0b0000100000000000) >> (11 - 9);
}
Golikov A.
что за бред?
если у вас на столе сбоит, на объекте будет полный пи....

Не та задача чтобы ее нельзя было решить без сбоя.

Уверены что сбоит не UART а энкодер? Если уарт добавьте проверку целостности, обвешайте протоколом. Если энкодер, то предложенный вариант считывания от АНТОХА (не знаю можно ли склонятьsm.gif) должен гарантированно отдавать правильное установившееся значение...
Alexsandr000
добавил проверку ошибки исчезли
Код
convert.INT = pos;
while(!(USART2->SR & USART_SR_TC)); //Проверяем установку флага TC - завершения предыдущей передачи
USART_SendData(USART2, convert.CHAR[1] & convert.CHAR[0]);
for(i = 0; i < 100; i++);
while(!(USART2->SR & USART_SR_TC)); //Проверяем установку флага TC - завершения предыдущей передачи
USART_SendData(USART2, convert.CHAR[0]);
for(i = 0; i < 100; i++);
while(!(USART2->SR & USART_SR_TC)); //Проверяем установку флага TC - завершения предыдущей передачи
USART_SendData(USART2, convert.CHAR[1]);
for(i = 0; i < 100; i++);
while(!(USART2->SR & USART_SR_TC)); //Проверяем установку флага TC - завершения предыдущей передачи
USART_SendData(USART2, convert.CHAR[1] | convert.CHAR[0]);
AHTOXA
Цитата(Golikov A. @ Oct 22 2014, 14:35) *
Если энкодер, то предложенный вариант считывания от АНТОХА (не знаю можно ли склонятьsm.gif) должен гарантированно отдавать правильное установившееся значение...

Склонять можноsm.gif
Автор уже применил мой вариант. Если продолжает сбоить, то проблема не в неатомарности чтения энкодера, а в чём-то другом.
Возможно, действительно сбой связи по UART. Может быть дребезг контактов энкодера (не знаю, возможно ли такое).
----
UPD О, решилась проблема. Ну и хорошо.
Для просмотра полной версии этой страницы, пожалуйста, пройдите по ссылке.
Invision Power Board © 2001-2025 Invision Power Services, Inc.