Pull to refresh

STM32 и FreeRTOS. 4. Шаг в сторону HAL

Reading time 5 min
Views 83K
HAL 9000: I'm completely operational, and all my circuits are functioning perfectly.
или это должно быть первой статьей, но я почему-то всегда пишу подобное ближе к концу

Раньше было про потоки, про семафоры и очереди

Одним из основных препятствий для перехода на STM32 является обилие текстов, инструкций и мануалов, описывающих работу с контроллером. Виновником этого обилия стала сама STMicroelectronics, которая поначалу планомерно запутывала своих пользователей, а затем предлагала неверные варианты выхода.

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

Но не так давно ST поняла, в какую яму она угодила и начала усиленно из нее выбираться, привлекая новые силы. И именно благодаря этому сейчас время старта сократилось до несуразно маленьких величин. Как это выглядит на практике? Добро пожаловать под кат.

Для начала дам совет, как быстро определить то, что не стоит читать про STM32.

Во-первых, год публикации раньше 2011-2012. Просто поверьте, что «то, что тогда» и «то, что сейчас» — две большие разницы. SPL и HAL в мире STM не просто очередные аббревиатуры.

Во-вторых, если встретится подобный код (это всего лишь кусочек инициализации ADC, если это кому-либо интересно).

RCC->APB2ENR |= RCC_APB2ENR_ADC1EN; 

ADC1->SMPR2 |= ADC_SMPR2_SMP0 | ADC_SMPR2_SMP1 
        | ADC_SMPR2_SMP2 | ADC_SMPR2_SMP3 | ADC_SMPR2_SMP4 
        | ADC_SMPR2_SMP5 | ADC_SMPR2_SMP6 | ADC_SMPR2_SMP7;     

ADC1->SQR1 |= ADC_SQR1_L_2 | ADC_SQR1_L_1 | ADC_SQR1_L_0;
        
ADC1->SQR2 |= ADC_SQR2_SQ8_2 | ADC_SQR2_SQ8_1 | ADC_SQR2_SQ8_0 
        | ADC_SQR2_SQ7_2 | ADC_SQR2_SQ7_1;           

В итоге после просмотра такого… великолепия большинство со словами «да ну его нафиг, я уж как-нибудь с pinMode проживу, чем такое городить» закидывало купленную на всякий случай плату в дальний угол ящика стола. Не скрою, я сам таким же был и только большая нужда заставила меня изучать что же скрыто за аббревиатурами CMSIS и SPL. Повторять мой путь сейчас нет совершенно никакого смысла (да и я постарался забыть большую часть того, чего прочитал), ну разве что если у вас очень большое желание добраться до потрохов. Поэтому здесь я рассказываю для тех, кому надо ехать.

Итак, в чем проблема? Главная и единственная проблема — это инициализация периферии. Вон те шаманские пляски с битами возникли не на пустом месте. И 99% проблем «у меня не работает» заключаются именно в неправильной инициализации. Не те частоты, попытка использовать выделенное под другое ресурсы и так далее и тому подобное. Что особо обостряет проблему, так это то, что никаких кодов ошибок или там эксепшенов нет. Все как у сапера: что-то сделал не так — все мертвое.

Вам кажется, что это сказки? Вот вам рисуночек схемы тактирования STMF3


Подсчетом мест, где можно ошибиться, предлагаю заняться самостоятельно


В итоге ST выкатила сначала SPL (Standard Peripheral Library), которая хоть улучшила ситуацию, но не сильно. А затем появилась следующая версия, которую обозвали HAL. А что бы еще больше облегчить разработчикам жизнь, была обновлена утилита STM32CubeMX, которая позволила буквально парой кликов мышки сгенерировать код инициализации для всего многообразия плат, процессоров и периферии. А рядышком положила так называемые firmware, в которые запихнула кучу примеров работы именно для данного процессора.

И то, что нажатием двух кнопок можно получить 100% (я ни разу пока не встретился с обратным) рабочий код абсолютно давит мысли «там оптимизировать и оптимизировать»…

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

Вот на рисунке слева я показал список функций, которые можно повесить на одну-единственную ножку. После подсчета обычно AVRщики (где 4 функции на ножку — максимум) долго хмыкают и что-то подсчитывают в уме. А когда я им говорю, что таких ножек может быть больше 160 штук, то вообще выпадают в осадок.

Но больше всего меня радует тот факт, что теперь благодаря HAL не надо переписывать код работы с периферией. Если где-то написано

НAL_GPIO_WritePin(GPIOA,GPIO_PIN_2,GPIO_PIN_RESET); 

То я знаю, что на всех доступных на данный момент мне процессорах этот код переведет ножку PA2 в «ноль». И даже написанная функция взятия среднего значения нужного канала АЦП тоже не будет сопротивляться при переходе с L1 на F4

int GetADCValue(uint32_t Channel,uint32_t Count)
{
	int val = 0;
	ADC_ChannelConfTypeDef   sConfig;
	sConfig.Channel=Channel;
	sConfig.Rank=1;
	sConfig.SamplingTime=10;
	HAL_ADC_ConfigChannel(&hadc,&sConfig);
	
	for(int i = 0; i < Count; i++)
	{
		HAL_ADC_Start(&hadc);
		HAL_ADC_PollForConversion(&hadc,1);
		val += HAL_ADC_GetValue(&hadc);
	}
return val / Count;
}

А теперь немного положенной по канонам магии STM32

Как вы думаете, сколько мне придется приложить усилий, что бы получить из STM32F3 ком-порт в usb, который просто будет возвращать то, что в него послали? Я тут подсчитал — 12 нажатий мышкой (включить USB, выбрать CDC, сгенерировать) и 6 строчек кода в функции CDC_Receive_FS (файл usbd_cdc_if.с)

for (int i = 0; i < *Len; i++)
        UserTxBufferFS[i] = UserRxBufferFS[i];

USBD_CDC_SetTxBuffer(&hUsbDeviceFS, &UserTxBufferFS[0], *Len);
USBD_CDC_TransmitPacket(&hUsbDeviceFS);
    
 USBD_CDC_SetRxBuffer(&hUsbDeviceFS, &UserRxBufferFS[0]);
 USBD_CDC_ReceivePacket(&hUsbDeviceFS); 

И примерно так же и с той же сложностью реализуется «звуковая карта», «флешка» или HID. Да, дадут только код инициализации, логику самому писать надо будет, но и это — офигенное подспорье.

Я даже выкладывать проект не буду, ибо толку тащить то, что надеюсь и так уже есть у Вас на компе, но результат покажу.



На вопрос «где бы примерчик взять?» ответ простой — в скачиваемой при генерации «фирмвари» лежит куча примеров. И рекомендую накачать (в менюшке найдете) «фирмварь» для других моделей процессоров — очень часто примеры не пересекаются и тот же «АЦП через DMA» есть только в одном месте, хотя прекрасно работает и там и там.

Отдельным абзацем замечу, что STM32Cube хоть и помогает программисту в быстром старте, она по прежнему требует понимания, что же настраивается и почему именно так настраивается. К примеру, по умолчанию прерывания и DMA для USART выключены, поэтому некоторые функции просто не будут работать. В общем, тщательно смотрите за вкладочками, благо программа позволяет перегенерировать проект, сохранив уже написанное.

Где-то здесь программисты обычно уже уходят «поуши» в генерацию «чего-то этакого», смену дескрипторов, размеров буферов и смены отображаемого названия в диспетчере задач на «Super Cool Device». Ну и я оставлю Вас с этим

Завершаем и приносим пользу
Tags:
Hubs:
+20
Comments 18
Comments Comments 18

Articles