Подключение энкодера к микроконтроллеру STM32f103.

В данном уроке мы с Вами подключим энкодер к микроконтроллеру  STM32F103.

Алгоритм работы энкодера довольно простой:
При кручении ручки влево или в право у нас на выходе меняются состояния логических уровней, считывая эти состояния мы и определяем направление вращения ручки.

Мы с Вами напишем два разных кода, один будет работать с использованием прерывания, второй будет работать в основном цикле. 

Программный код с использованием прерывание:

// Максимальное значение уровня громкости
#define VOLUME_MAX_VAL  51
 
// Текущий уровень громкости
volatile uint8_t nVol = 10;
 
 
 
int main(void) {
    // Включаем тактирование модуля ввода-вывода 'B'
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
     
    // Переменные (структуры) для инициализации
    GPIO_InitTypeDef GPIO_InitStruct;
    EXTI_InitTypeDef EXTI_InitStruct;
    NVIC_InitTypeDef NVIC_InitStruct;
 
    // Прерывания - это альтернативная функция порта, включаем
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);
 
    // Настройка пинов B0 и B1
    GPIO_InitStruct.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1;
    GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IPU;      // Вход с подтяжкой вверх
    GPIO_InitStruct.GPIO_Speed = GPIO_Speed_2MHz;
    GPIO_Init(GPIOB, &GPIO_InitStruct);
 
    // Добавляем вектор прерывания
    NVIC_InitStruct.NVIC_IRQChannel = EXTI0_IRQn;
    // Устанавливаем приоритет
    NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 0x08;   // 0x00 - 0x0F
    NVIC_InitStruct.NVIC_IRQChannelSubPriority = 0x08;
    // Разрешаем прерывание
    NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;
    NVIC_Init(&NVIC_InitStruct);
 
    // PB0 подключен к EXTI_Line0
    GPIO_EXTILineConfig(GPIO_PortSourceGPIOB, GPIO_PinSource0);
    EXTI_InitStruct.EXTI_Line = EXTI_Line0;
    // Разрешаем прерывание
    EXTI_InitStruct.EXTI_LineCmd = ENABLE;
    // По спаду
    EXTI_InitStruct.EXTI_Mode = EXTI_Mode_Interrupt;
    EXTI_InitStruct.EXTI_Trigger = EXTI_Trigger_Falling;
    EXTI_Init(&EXTI_InitStruct);
     
 
    while(1) {
        // * * *
        }
}
 
 
 
// Обработчик прерывания для энкодера по спаду PB0
void EXTI0_IRQHandler(void) {
    uint32_t nCount = 3;
 
    // Убеждаемся, что флаг прерывания установлен
    if (EXTI_GetITStatus(EXTI_Line0) != RESET) {
        if (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_1) == 1) {
            // Кто-то крутит ручку по часовой стрелке
            if(nVol < VOLUME_MAX_VAL) {
                nVol++;
                }
            }
        else {
            // Ручку крутят против часовой стрелки
            if(nVol > 0) {
                nVol--;
                }
            }
 
        for(nCount *= 100000; nCount; nCount--);
 
        // Сбрасываем флаг прерывания
        EXTI_ClearITPendingBit(EXTI_Line0);
        }
}

#define FORWARD 0
#define BACKWARD 1

#define NOREADY 0
#define READY 1
#define INIT 3

volatile uint8_t encoder_status = INIT;
volatile uint8_t encoder_direction = FORWARD;

void usart_init(void)
{
/* Enable USART1 and GPIOA clock */
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1 | RCC_APB2Periph_GPIOA, ENABLE);

/* Configure the GPIOs */
GPIO_InitTypeDef GPIO_InitStructure;

/* Configure USART1 Tx (PA.09) as alternate function push-pull */
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);

/* Configure USART1 Rx (PA.10) as input floating */
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_Init(GPIOA, &GPIO_InitStructure);

/* Configure the USART1 */
USART_InitTypeDef USART_InitStructure;

/* USART1 configuration ——————————————————*/
/* USART1 configured as follow:
— BaudRate = 115200 baud
— Word Length = 8 Bits
— One Stop Bit
— No parity
— Hardware flow control disabled (RTS and CTS signals)
— Receive and transmit enabled
— USART Clock disabled
— USART CPOL: Clock is active low
— USART CPHA: Data is captured on the middle
— USART LastBit: The clock pulse of the last data bit is not output to
the SCLK pin
*/
USART_InitStructure.USART_BaudRate = 115200;
USART_InitStructure.USART_WordLength = USART_WordLength_8b;
USART_InitStructure.USART_StopBits = USART_StopBits_1;
USART_InitStructure.USART_Parity = USART_Parity_No;
USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;

USART_Init(USART1, &USART_InitStructure);

/* Enable USART1 */
USART_Cmd(USART1, ENABLE);
}

void USARTSend(const unsigned char *pucBuffer)
{
while (*pucBuffer)
{
USART_SendData(USART1, *pucBuffer++);
while(USART_GetFlagStatus(USART1, USART_FLAG_TC) == RESET)
{
}
}
}

void encoder_init(void)
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);

//Канали TIM3_CH1, TIM3_CH2 як вхід з підтяжкою
GPIO_InitTypeDef gpio_cfg;
gpio_cfg.GPIO_Mode = GPIO_Mode_IPU;
gpio_cfg.GPIO_Pin = GPIO_Pin_6 | GPIO_Pin_7;
gpio_cfg.GPIO_Speed = GPIO_Speed_2MHz;
GPIO_Init(GPIOA, &gpio_cfg);

RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);

// Налаштовуємо TIM3
TIM_TimeBaseInitTypeDef TIMER_InitStructure;
TIM_TimeBaseStructInit(&TIMER_InitStructure);
// Встановлюємо TIM_Period = 1. Таймер рахуватиме до 1. Переривання буде викликатися при кожній зміні положення енкодера
TIMER_InitStructure.TIM_Period = 1;
TIMER_InitStructure.TIM_CounterMode = TIM_CounterMode_Up | TIM_CounterMode_Down;
TIM_TimeBaseInit(TIM3, &TIMER_InitStructure);

// Налаштовуємо Encoder Interface та дозволяємо переривання
TIM_EncoderInterfaceConfig(TIM3, TIM_EncoderMode_TI12, TIM_ICPolarity_Rising, TIM_ICPolarity_Rising);
TIM_ITConfig(TIM3, TIM_IT_Update, ENABLE);
TIM_Cmd(TIM3, ENABLE);

NVIC_EnableIRQ(TIM3_IRQn);
}

void TIM3_IRQHandler(void)
{
if (TIM_GetITStatus(TIM3, TIM_IT_Update) != RESET)
{
TIM_ClearITPendingBit(TIM3, TIM_IT_Update);

// Перше спрацювання відкидаємо. encoder_status == INIT може бути тільки один раз
if (encoder_status == INIT)
encoder_status = NOREADY;
else
encoder_status = READY;

/* У регістрі TIM3_CR1 біт TIM_CR1_DIR буде напрямок обертання енкодера*/
encoder_direction = (TIM3->CR1 & TIM_CR1_DIR ? BACKWARD : FORWARD);
}
}

int main(void)
{
char buffer[80] = {`\0`};

usart_init();
encoder_init();

while (1)
{
if (encoder_status)
{
encoder_status = NOREADY;

if (encoder_direction == FORWARD){
sprintf(buffer, «FORWARD\r\n»);
//….
}
else{
sprintf(buffer, «BACKWARD\r\n»);
//….
}
USARTSend(buffer);
}
}
}