STM32 Часть 3: Первый Проект

Моя цель - предложение широкого ассортимента товаров и услуг на постоянно высоком качестве обслуживания по самым выгодным ценам.
Мы наблюдаем общество, которое все больше зависит от машин, но при этом использует их все неэффективнее. — Douglas Rushkoff


Эта фраза должна служить мотивацией для каждого программиста. Ведь именно вы решаете как машина использует свои ресурсы. Но как и с начала времен, человек вверяет свое право решать третьим лицам взамен легкого пути. Перед тем как спрашивать меня о пользе моих статей, когда есть «Куб», задайте вопрос себе, почему «куб» решает за меня.

STM32 Часть 1: Основы
STM32 Чаcть 2: Инициализация

Итак, продолжим наше приключение. Мы уже написали скрипт инициализации, разобрались с линкером и компилятором. Настало время мигнуть светодиодом. В этой статье мы бегло пробежимся по основам блока RCC и GPIO, а также добавим парочку хедеров, которые мы будем использовать в следующих проектах. Поехали.

RCC — Reset and Clock Control, блок регистров которые управляют тактированием процессора и периферии. Этим блоком управляются несколько источников тактирования: HSI (High speed internal), HSE (high speed external), PLL/PLLSAI/PLLI2S (Phased loop lock). Все эти три источника тактовой частоты могут быть использованы процессором, конечно же при правильной настройки регистров блока RCC. Об этом мы поговорим подробно в следующей статье.

GPIO — General Purpose Input and Output. Блок регистров который создан для мигания светодиодами.То есть это блок для управления ногами нашего мк, портал в реальный мир. В отличии от всем знакомого Atmega328p (Arduino), stm32 предлагает более широкую настройку ножек. К примеру функция ноги Open Drain недоступна на меге. Также контроль скорости, с которой мк поднимает вольтаж на ноге, может быть настроен.

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

Первое что нам понадобиться это адреса этих блоков, базовые. Начнем с блока RCC, и создадим хедер rcc.h. В нем мы создадим macro RCC_BASE, а также опишем структуру блока регистров, и создадим указатель на структуру.

rcc.h

#define RCC_BASE   0x40023800

typedef struct
{
  volatile unsigned int CR;           
  volatile unsigned int PLLCFGR;      
  volatile unsigned int CFGR;         
  volatile unsigned int CIR;          
  volatile unsigned int AHB1RSTR;     
  volatile unsigned int AHB2RSTR;     
  volatile unsigned int AHB3RSTR;     
  unsigned int          RESERVED0;    
  volatile unsigned int APB1RSTR;     
  volatile unsigned int APB2RSTR;     
  unsigned int          RESERVED1[2]; 
  volatile unsigned int AHB1ENR;      
  volatile unsigned int AHB2ENR;      
  volatile unsigned int AHB3ENR;      
  unsigned int          RESERVED2;    
  volatile unsigned int APB1ENR;      
  volatile unsigned int APB2ENR;      
  unsigned int          RESERVED3[2]; 
  volatile unsigned int AHB1LPENR;    
  volatile unsigned int AHB2LPENR;    
  volatile unsigned int AHB3LPENR;    
  unsigned int          RESERVED4;    
  volatile unsigned int APB1LPENR;    
  volatile unsigned int APB2LPENR;    
  unsigned int          RESERVED5[2]; 
  volatile unsigned int BDCR;         
  volatile unsigned int CSR;          
  unsigned int          RESERVED6[2]; 
  volatile unsigned int SSCGR;        
  volatile unsigned int PLLI2SCFGR;   
  volatile unsigned int PLLSAICFGR;   
  volatile unsigned int DCKCFGR1;     
  volatile unsigned int DCKCFGR2;     

} RCC_Struct;

#define RCC ((RCC_Struct *) RCC_BASE)


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

gpio.h

#define GPIO_BASE   0x40020000
#define GPIO_OFFSET 0x400

typedef struct
{
   volatile unsigned int MODER;   
   volatile unsigned int OTYPER;  
   volatile unsigned int OSPEEDR; 
   volatile unsigned int PUPDR;   
   volatile unsigned int IDR;     
   volatile unsigned int ODR;     
   volatile unsigned int BSRR;    
   volatile unsigned int LCKR;    
   volatile unsigned int AFR[2];  

} GPIO_Struct;


Давайте разберем в чем тут дело и зачем нам структуры. Дело в том что мы можем обращаться к регистру через указатель на структуру, нам не надо ее инициализировать так как физически она уже существует. К примеру:

RCC->AHB1ENR = 0; 


Это экономит место в памяти, а иногда и вообще его не требует. Но об экономии не сегодня.

Так два хедера у нас готовы, осталось узнать как дрыгнуть ногой при помощи этих регистров и создать int main();. Тут все просто и не совсем. В STM32 для доступа к блоку регистров мы должны сначала подать на него тактирование, а иначе как данные до него доедут. Я не буду сейчас углубляться в строение шины, а просто скажу как есть. Блоки размещены на разных шинах. Наш блок находиться на шине AHB1. То есть нам надо включить определеный порт на шине AHB1, в моем случае это порт I. Для начала нам конечно понадобиться функция main.

Давайте немного обновим наш стартап файл и добавим в него int main();. а после создадим и сам файл main.c

extern void *_estack;

void Reset_Handler();
void Default_Handler();

// Форвард декларация тут
int  main();

void NMI_Handler()                    __attribute__ ((weak, alias ("Default_Handler")));
void HardFault_Handler()              __attribute__ ((weak, alias ("Default_Handler")));

void *vectors[] __attribute__((section(".isr_vector"), used)) = {
    &_estack,
	&Reset_Handler,
	&NMI_Handler,
	&HardFault_Handler
};

extern void *_sidata, *_sdata, *_edata, *_sbss, *_ebss;



void __attribute__((naked, noreturn)) Reset_Handler()
{


	void **pSource, **pDest;
	for (pSource = &_sidata, pDest = &_sdata; pDest != &_edata; pSource++, pDest++)
		*pDest = *pSource;

	for (pDest = &_sbss; pDest != &_ebss; pDest++)
		*pDest = 0;

    //Мейн фнкция добавлена тут
    main();

	while(1);
}

void __attribute__((naked, noreturn)) Default_Handler(){
    while(1);
}


А теперь создаем и сам файл main.c. В файле я постарался расписать все в комментариях к коду, поэтому читаем и вникаем. если есть вопросы пишите в коменты ниже я отвечу.

#include "rcc.h"
#include "gpio.h"


int main()
{
    //Включаем тактирование GPIO I порта, порт номер 8 (очень важно)
    RCC->AHB1ENR |= (1<<8);

    //Структура для обращения к регистрам порта
    //Используем офсет умноженый на номер порта что дает нам адресс порта
    //В дальнейшем нам такой подход очень пригодится
    volatile GPIO_Struct *GPIOI = (GPIO_Struct *)(GPIO_BASE + (GPIO_OFFSET*8));  

    //Далее настраиваем режим ножки на "Выход"
    GPIOI->MODER |= (1<<2);

    //Теперь указываем тип выхода, в данном примере, push-pull, необходимости указывать нет.
    //По умолчанию стоит push pull
    GPIOI->OTYPER &= ~(1<<1);

    //Скорость с которой будет происходить переключение ножки, очень важный парметр при работе с переферией
    GPIOI->OSPEEDR |= (2<<2);

    //И теперь наш цикл
    while(1){
        for(volatile int i = 0; i < 1000000; i++){
            // Задержка
        }

        //Перпеключаем светодиод, если был 1 то будет 0 и наоброт.
        GPIOI->ODR ^= (1<<1);
    }

    return 0;
}


Теперь бы нам собрать проект и закинуть его на мк, проверки ради. Я выложил репозиторий на Github все что нужно сделать так это запустить Make утилиту.
make


Всем спасибо за внимание, в следующей статье подробнее поговорим о блоке RCC и как с ним работать.
Источник: https://habr.com/ru/post/491406/


Интересные статьи

Интересные статьи

В сентябре 2019 года я стартовала социальный Open Source проект Brain Up. Это веб-приложение, которое включает в себя серии интерактивных аудиоупражнений для тренировки с...
Часть 1 ‣ Часть 2 ‣ Часть 3 ‣ Часть 4 ‣ Часть 5 ‣ Часть 6 ‣ Часть 7 ‣ Часть 8 ‣ Часть 9 Starlink и Пентагон С момента объявления Илоном Маском о проекте Starlink с 4425 спутника...
Меня зовут Александра Царева. Я и мои коллеги работаем над проектами в сфере компьютерного зрения в Центре машинного обучения компании «Инфосистемы Джет». Мне хочется поделиться наш...
C++ сложный и интересный язык, совершенствоваться в нем можно чуть ли не всю жизнь. В какой-то момент мне захотелось изучать его следующим образом: взять какой-то аспект языка, возможно довольно ...
Тема статьи навеяна результатами наблюдений за методикой создания шаблонов различными разработчиками, чьи проекты попадали мне на поддержку. Порой разобраться в, казалось бы, такой простой сущности ка...