-Поиск по дневнику

Поиск сообщений в rss_rss_hh_new

 -Подписка по e-mail

 

 -Статистика

Статистика LiveInternet.ru: показано количество хитов и посетителей
Создан: 17.03.2011
Записей:
Комментариев:
Написано: 51


STM32 без HAL и SPL

Понедельник, 11 Сентября 2017 г. 11:19 + в цитатник
sanders1967 сегодня в 11:19 Разработка

STM32 без HAL и SPL

    В свое время, более 5 лет, при поиске информации о 32-разрядных микроконтроллерах, обратил внимание, что практически все примеры для STM32 подразумевали использование SPL (Standard Peripherals Library). Цитата из Википедии:
    STM32F10x Standard Peripherals Library (сокр. STM32F10x SPL) — библиотека, созданная компанией STMicroelectronics на языке Си для своих микроконтроллеров семейства STM32F10x. Содержит функции, структуры и макросы для облегчения работы с периферией микроконтроллера."

    В настоящее время, для снижения порога вхождения и ускорения разработки предлагается использовать STM32CUBE. Цитата с сайта STM:
    STM32Cube embedded software libraries, including:
    The HAL hardware abstraction layer, enabling portability between different STM32 devices via standardized API calls
    The Low-Layer (LL) APIs, a light-weight, optimized, expert oriented set of APIs designed for both performance and runtime efficiency
    A collection of Middleware components, like RTOS, USB library, file system, TCP/IP stack, Touch sensing library or Graphic Library (depending on the MCU series)

    На мой взгляд, для большинства проектов не нужны внешние библиотеки и проще использовать обращение к регистрам микроконтроллера, используя стандартную документацию.

    Бытует мнение, что использование регистров микроконтроллера — это более сложный путь, чем использовать обертки из внешних библиотек. Попробую показать, что это не всегда так.
    Примеры инициализации регистров с комментариями.

    Инициализация периферии.


    Порты

    .
    Для многих проектов нужно просто включать или выключать соответствующие ноги контроллера и считывать аналоговые значения.

    Включение порта: 1 строчка кода:
    //инициализация порта A*************************
    RCC->AHB1ENR |= RCC_AHB1ENR_GPIOAEN;

    Перевод 0 пина порта A в аналоговый режим 2 строчки:
    //PA0 - PA0/ADC1_ADC2_ADC3_IN0
    // GPIO_Pin_0 порта A аналоговый вход
    GPIOA->MODER |= GPIO_MODER_MODER0_0;
    GPIOA->MODER |= GPIO_MODER_MODER0_1;

    Перевод 2 пина порта A в режим выхода (пуш-пулл) 1 строчка:
    GPIOA->MODER |= GPIO_MODER_MODER2_0;

    Использование альтернативных функций тоже не очень сложно.
    Часто микроконтроллер используется для управления полумостовым преобразователем. Для этого нужно соответствующие пины порта сконфигурировать, как комплементарные выходы ШИМ.
    Определение выхода 8 порта А, как ШИМ выход CH1 счетчика TIM1 (3 строчки):
    //PA8/TIM1_CH1
    // Alternate function mode
    GPIOA->MODER &= ~GPIO_MODER_MODER8_0; //0
    GPIOA->MODER |= GPIO_MODER_MODER8_1; //1
    //GPIO alternate function high register (GPIOx_AFRL)
    //AFR8[3:0] = 0001: AF1
    GPIOA -> AFR[1] |= 0x00000001;

    Определение выхода 13 порта B, как ШИМ выход CH1N счетчика TIM1:
    //PB13/TIM1_CH1N
    // Alternate function mode
    GPIOB->MODER &= ~GPIO_MODER_MODER13_0; //0
    GPIOB->MODER |= GPIO_MODER_MODER13_1; //1
    //GPIO alternate function high register (GPIOx_AFRL)
    //AFR13[3:0] = 0001: AF1
    GPIOB -> AFR[1] |= 0x00100000;


    Закономерный вопрос: где взять обозначения нужных регистров и битов? Ответ: в 3-ех документах (на примере 746).
    1. Reference manual STM32F7.pdf
    2. STM32F745xx.pdf
    3. stm32f746xx.h
    Этих 3 файлов полностью хватает для того, чтобы правильно обращаться ко всем регистрам микроконтроллера.

    АЦП

    .
    Пример инициализации АЦП для работы в режиме DMA. В этом режиме 4 канала АЦП2 автоматически переключаются по кругу и передают данные контроллеру DMA, который складывает данные в массив.
    void init_ADC1(void)
    {
    RCC->APB2ENR |= RCC_APB2ENR_ADC1EN; //подаем тактирование АЦП
    ADC1->CR2 |= ADC_CR2_ADON; //включить АЦП
    ADC1->CR1 |= ADC_CR1_EOCIE;
    ADC1->CR1 |= ADC_CR1_SCAN; // Bit 8 SCAN: Scan mode
    ADC1->CR2 |= ADC_CR2_EOCS; //Bit 10 EOCS: End of conversion selection
    ADC1->CR2 |= ADC_CR2_DMA; //Bit 8 DMA: Direct memory access mode (for single ADC mode)
    ADC1->CR2 |= ADC_CR2_DDS; //Bit 9 DDS: DMA disable selection (for single ADC mode

    //Bits 23:20 L[3:0]: Regular channel sequence length (4)
    //0003: 4 conversion
    ADC1->SQR1 |= ADC_SQR1_L_0; //1
    ADC1->SQR1 |= ADC_SQR1_L_1; //1
    ADC1->SQR1 &= ~ADC_SQR1_L_2; //0
    ADC1->SQR1 &= ~ADC_SQR1_L_3; //0

    //Bits 4:0 SQ1[4:0]: 1st conversion in regular sequence PC0/ADC1_ADC2_ADC3_IN10
    ADC1->SQR3 &= ~ADC_SQR3_SQ1_0; //0
    ADC1->SQR3 |= ADC_SQR3_SQ1_1; //1
    ADC1->SQR3 &= ~ADC_SQR3_SQ1_2; //0
    ADC1->SQR3 |= ADC_SQR3_SQ1_3; //1
    ADC1->SQR3 &= ~ADC_SQR3_SQ1_4; //0

    //Bits 4:0 SQ2[4:0]: 2st conversion in regular sequence PC1/ADC1_ADC2_ADC3_IN11
    ADC1->SQR3 |= ADC_SQR3_SQ2_0; //1
    ADC1->SQR3 |= ADC_SQR3_SQ2_1; //1
    ADC1->SQR3 &= ~ADC_SQR3_SQ2_2; //0
    ADC1->SQR3 |= ADC_SQR3_SQ2_3; //1
    ADC1->SQR3 &= ~ADC_SQR3_SQ2_4; //0

    //Bits 4:0 SQ3[4:0]: 3st conversion in regular sequence PC2/ADC1_ADC2_ADC3_IN12
    ADC1->SQR3 &= ~ADC_SQR3_SQ3_0; //0
    ADC1->SQR3 &= ~ADC_SQR3_SQ3_1; //0
    ADC1->SQR3 |= ADC_SQR3_SQ3_2; //1
    ADC1->SQR3 |= ADC_SQR3_SQ3_3; //1
    ADC1->SQR3 &= ~ADC_SQR3_SQ3_4; //0

    //Bits 4:0 SQ4[4:0]: 4st conversion in regular sequence PC3/ADC1_ADC2_ADC3_IN13
    ADC1->SQR3 |= ADC_SQR3_SQ4_0; //1
    ADC1->SQR3 &= ~ADC_SQR3_SQ4_1; //0
    ADC1->SQR3 |= ADC_SQR3_SQ4_2; //1
    ADC1->SQR3 |= ADC_SQR3_SQ4_3; //1
    ADC1->SQR3 &= ~ADC_SQR3_SQ4_4; //0

    NVIC_EnableIRQ (ADC_IRQn);
    }

    ПДП

    .
    Инициализация одного потока ПДП для передачи данных из АЦП в массив (см. выше)
    //Тактирование DMA2
    RCC->AHB1ENR |= RCC_AHB1ENR_DMA2EN;

    //Stream3 Channel 1 DMA2 - в массив ADC2_array 2 каналa

    //Bits 27:25 CHSEL[2:0]: Channel selection (1)
    DMA2_Stream3->CR |= DMA_SxCR_CHSEL_0; //1
    DMA2_Stream3->CR &= ~DMA_SxCR_CHSEL_1; //0
    DMA2_Stream3->CR &= ~DMA_SxCR_CHSEL_2; //0

    //Bits 14:13 MSIZE[1:0]: Memory data size (16 bit)
    DMA2_Stream3->CR |= DMA_SxCR_MSIZE_0; //1
    DMA2_Stream3->CR &= ~DMA_SxCR_MSIZE_1; //0

    //Bits 12:11 PSIZE[1:0]: Peripheral data size (16 bit)
    DMA2_Stream3->CR |= DMA_SxCR_PSIZE_0; //1
    DMA2_Stream3->CR &= ~DMA_SxCR_PSIZE_1; //0

    //Bits 10 MINC: Memory increment mode
    DMA2_Stream3->CR |= DMA_SxCR_MINC;

    //Bits 7:6 DIR[1:0]: Data transfer direction (00: Peripheral-to-memory)
    DMA2_Stream3->CR &= ~DMA_SxCR_DIR_0; //0
    DMA2_Stream3->CR &= ~DMA_SxCR_DIR_1; //0

    //Bits 4 TCIE: Transfer complete interrupt enable
    DMA2_Stream3->CR |= DMA_SxCR_TCIE;

    //Bits 15:0 NDT[15:0]: Number of data items to transfer
    //1000 point x 4 channel
    DMA2_Stream3-> NDTR = 4000;

    //Bits 31:0 PAR[31:0]: Peripheral address
    DMA2_Stream3->PAR = (uint32_t) &(ADC2->DR);

    //Bits 31:0 M0A[31:0]: Memory 0 address
    DMA2_Stream3->M0AR = (uint32_t) ADC2_array;

    //Bits 0 EN: Stream enable / flag stream ready when read low
    DMA2_Stream3->CR |= DMA_SxCR_EN;

    NVIC_EnableIRQ (DMA2_Stream0_IRQn);


    Таймеры

    .
    Пример конфигурации таймера с комплементарными 12-разрядными ШИМ выходами для управления 3 полумостами (3-фазный инвертор)
    // TIM1 PWM
    RCC -> APB2ENR |= RCC_APB2ENR_TIM1EN; //тактирование TIM1

    TIM1->CR1 |= TIM_CR1_CMS_0; //Center-aligned mode 1
    TIM1->CR1 |= TIM_CR1_ARPE;

    //частота ШИМ
    //прескалер 8 и период 4000 - 3000 Гц
    //частота шины 108 МГц

    TIM1->PSC = 8;
    TIM1->ARR = 4000;

    TIM1->CCR1 = 1000; //начальные значения
    TIM1->CCR2 = 1000;
    TIM1->CCR3 = 1000;

    TIM1->CCMR1 &= ~TIM_CCMR1_OC1M_0;
    TIM1->CCMR1 |= TIM_CCMR1_OC1M_1;
    TIM1->CCMR1 |= TIM_CCMR1_OC1M_2; //110: PWM mode 1

    TIM1->CCMR1 &= ~TIM_CCMR1_OC2M_0;
    TIM1->CCMR1 |= TIM_CCMR1_OC2M_1;
    TIM1->CCMR1 |= TIM_CCMR1_OC2M_2; //110: PWM mode 1

    TIM1->CCMR2 &= ~TIM_CCMR2_OC3M_0;
    TIM1->CCMR2 |= TIM_CCMR2_OC3M_1;
    TIM1->CCMR2 |= TIM_CCMR2_OC3M_2; //110: PWM mode 1

    TIM1->CCER |= TIM_CCER_CC1E; // Capture/Compare 1 output enable
    TIM1->CCER |= TIM_CCER_CC1NE; // Capture/Compare 1 complementary output enable
    TIM1->CCER |= TIM_CCER_CC2E; // Capture/Compare 2 output enable
    TIM1->CCER |= TIM_CCER_CC2NE; // Capture/Compare 2 complementary output enable
    TIM1->CCER |= TIM_CCER_CC3E; // Capture/Compare 3 output enable
    TIM1->CCER |= TIM_CCER_CC3NE; // Capture/Compare 3 complementary output enable

    //DTG[7:0]: Dead-time generator setup 1 mks
    TIM1->BDTR |= TIM_BDTR_DTG_0;
    TIM1->BDTR |= TIM_BDTR_DTG_1;
    TIM1->BDTR |= TIM_BDTR_DTG_2;
    TIM1->BDTR |= TIM_BDTR_DTG_3;
    TIM1->BDTR |= TIM_BDTR_DTG_4;
    TIM1->BDTR |= TIM_BDTR_DTG_5;
    TIM1->BDTR |= TIM_BDTR_DTG_6;
    TIM1->BDTR |= TIM_BDTR_DTG_7;

    TIM1->DIER |= TIM_DIER_CC1IE; //Capture/Compare 1 interrupt enable
    //TIM1->DIER |= TIM_DIER_CC2IE; //Capture/Compare 2 interrupt enable
    //TIM1->DIER |= TIM_DIER_CC3IE; //Capture/Compare 3 interrupt enable

    TIM1->CR1 |= TIM_CR1_CEN; //Bit 0 CEN: Counter enable
    TIM1->BDTR |= TIM_BDTR_MOE; //MOE: Main output enable

    NVIC_EnableIRQ (TIM1_CC_IRQn); //разрешить прерывания от таймера


    Пример конфигурации таймера для формирования прерываний по времени.
    Прерывания от этого таймера, как правило, используются для обновления данных в
    интерфейсах. В этом режиме внешние выходы таймера не используются.
    // TIM3 100 мсек
    RCC -> APB1ENR |= RCC_APB1ENR_TIM3EN; //TIM3 Timer clock enable

    TIM3->CR1 |= TIM_CR1_CEN; //Bit 0 CEN: Counter enable
    TIM3->CR1 |= TIM_CR1_ARPE; //Bit 7 ARPE: Auto-reload preload enable
    TIM3->DIER |= TIM_DIER_UIE; //Bit 0 UIE: Update interrupt enable

    TIM3->PSC = 2000;
    TIM3->ARR = 5400;
    NVIC_EnableIRQ (TIM3_IRQn); //разрешить прерывания от таймера

    Эти файлы конфигурации используются практически без изменения длительное время. Начиналось это все еще на 103 контроллере, сейчас используется на 7 серии. :)
    Конечно инициализация АЦП, таймеров и ПДП чуть сложнее, чем портов, но тоже простая задача.
    При этом не используются внешние библиотеки, более компактный код, более предсказуемое поведение контроллера.
    Original source: habrahabr.ru (comments, light).

    https://habrahabr.ru/post/337622/

    Метки:  

     

    Добавить комментарий:
    Текст комментария: смайлики

    Проверка орфографии: (найти ошибки)

    Прикрепить картинку:

     Переводить URL в ссылку
     Подписаться на комментарии
     Подписать картинку