Как стать автором
Обновить

Спутниковый спидометр на STM32F1 и FreeRTOS

Время на прочтение8 мин
Количество просмотров68K
В статье речь пойдёт о самодельном спидометре, получающем сведения о текущей скорости автомобиля, его координатах и времени со спутников GPS. Статья также, надеюсь, поможет тем, что делает первые шаги в освоении 32х-разрядных микроконтроллеров с ядром Cortex M и хочет освоить одну из операционных систем реального времени (RTOS) для применения в своих наработках.

Аппаратная реализация


Сидел себе, писал на ассемблере под 16 и 18е pic’ки, да горя не знал. Но пришла пора двигаться дальше, изучить 32х разрядные микроконтроллеры. Выбор пал на STM32 (а именно – с ядром ARM Cortex M3) как наиболее перспективные – в рунете в геометрической прогрессии растёт объём статей по ним, микроконтроллеры весьма привлекальны в цене при большом объёме функциональных возможнотей, имеют большие перспективны. При росте проекта можно перевести работу на куда более производительные версии данного ядра – с Cortex M на Cortex A. К тому же, компания Apple в своих iPhone, iPad использует это же самое ядро – ARM Cortex. Только более мощное, «А».

В качестве отладочной платы, на которой и реализовал спутниковый спидометр, взял себе на ебее за ~70$ один из клонов HY Redbull:


Плата, помимо собственно знакомства с микроконтроллером STM32F103VCT6 (72МГц, 512Кб FLASH, 64kb SRAM, 3АЦП, 2ЦАП, DMA, FSMC, 5xUSART, 2xI2C, 3xSPI, CAN, USB 2.0 FS и др) и его встроенной периферией, позволяет освоить базовую распространённую внешнюю периферию — MP3/WMA-декодер VS1003, Ethernet-контроллер ENC28J60, LCD-экран 3.2” с тачскрином (контроллер SSD1289), SD-карты (под платой разъём), USB, CAN, RS232/485, контроллер CH376 (реализует интерфейс USB-Host/SD <-> UART/SPI/8bit parallel для подключения к МК sd-кард и USB-флешек).

Заливать прошивку в STM32 можно через UART, т.е. фактически программатор как таковой не требуется. Но гораздо удобнее пользоваться средствами отладки, для которых предназначен JTAG-порт на плате. Можно потратить на JTAG-отладчик ещё 20-50 долларов, но гораздо целесообразнее взять родную ST’овскую отладочную плату со встроенным отладчиком серии Discovery. Стоит от 10$ (и при этом это реально всё, базис того, что необходимо для освоения STM32). Я себе взял в качестве отладчика для JTAG отладочную плату STM32F4-Discovery (вообще говоря, досталась бесплатно как посетителю семинара, а так она примерно 600руб стоит):



— предназначена для отладки ARM Cortex M4 (STM32F407), на борту встроенный отладчик (джамперами можно переключить с отладки stm32f407 на отладку внешней платы по SWD, чем я и воспользовался, подключив по SWD свой firebull плату к JTAG-разъёму). Этот микроконтроллер уже на частоте 168МГц может работать (правда, камушек ревизии «А» попался, сыроват, в этой ревизии падение производительности происходит из-за акселератора памяти). Флеша 1Мб, ОЗУ 128КБ. Из встроенной периферии заслуживает внимания USB device/host/OTG, 10/100 Ethernet, интерфейс камеры (до 1Мпикс подключить можно), аппаратный подсчет CRC, DSP-процессор. Из внешней периферии на плате присутствует MEMS-акселерометр LIS302DL, цифровой MEMS-микрофон MP45DT02, АЦП с интерфейсом I2S CS43L22. К плате множество примеров идут, среди которых особо понравился диктофон-плеер, работающий с подключаемой usb-флешкой (демонстрация работы usb otg, mems-микрофона, audio i2s dac).
В плане выбора GPS-приёмника был неоригинален. Взял весьма распространённый EB-500. Чтобы не мучаться при распайке платы, взял сразу отладочную плату EB-500 EVB Kit с активной антенной в комплекте:

Сам приемник выводит данные через UART. Но благодаря UART-USB конвертеру на базе CP2102 плату сразу по USB подключить можно. В комплекте идет софтина EB View, демонстрираующая возможности чипа. Можно посмотреть вывод NMEA данных, задать перечень полей, которые нужно чипу выводить, периодичность их вывода, опробовать AGPS, да и много чего ещё. Можно через Serial Port Monitor посмотреть диалог общения EB View с eb-500, и реализовать соответствующую настройку eb-500 при старте своего девайса. Ну или изучить даташит) Также на плате уже присутствует 3х вольтовая батарейка для хранения альманаха, когда девайс выключен. Для последующего быстрого старта.
Также прикупил несколько шлейфов для коммутации этих плат — www.dealextreme.com/p/30cm-breadboard-wires-for-electronic-diy-40-cable-pack-80207#open%20full%20view – рекомендую, штука крайне полезная и удобная.


Программная реализация

Я выбрал себе среду Keil. Удобная, распространённая, снабжённая обилием примеров… Одним словом – «моё». :) Использовал прилагающуюся к плате Firebull библиотеку для работы с GLCD.

В качестве операционной системы реального времени (RTOS) выбрал FreeRTOS – по ней море литературы, она активно развивается, и за счет этих и других факторов более предпочтительным вариантом оказывается по сравнению с другими – uC/OS, scmOS и т.д. Плюс поддерживает как вытесняющий, так и корпоративный, и комбинированный режимы работы. А мне, как новичку, это в плюс. :) Если не знаком вообще с RTOS, но хочешь познакомиться на примере работы с FreeRTOS и начать использовать в своих проектах, то есть очень хорошая статья, которая мне помогла: microsin.net/programming/ARM/freertos-part1.html — все что нужно для старта, там описано. Далее остается скачать дистрибутив FreeRTOS, взять оттуда уже готовый пример под свой микроконтроллер, доработать, и наслаждаться его работой. Все оказывается не настолько уж сложным на деле, каковым кажется на первый взгляд. :)

А теперь рассмотрим сам исходный код спутникового спидометра, получающего данные с GPS-приёмника EB-500 и выводящего их на 3.2” ЖК-экран:


int main(void)
{
    prvSetupHardware();
    vSemaphoreCreateBinary( xBinarySemaphore );
    if (xBinarySemaphore != NULL){
       xTaskCreate( vLEDTask, ( signed char * ) NULL , LED_TASK_STACK_SIZE , NULL , LED_TASK_PRIORITY , NULL );
       xTaskCreate( vGPSTask, ( signed char * ) NULL , GPS_TASK_STACK_SIZE,NULL , GPS_TASK_PRIORITY , NULL );
       // Запускаем планировщик задач
       vTaskStartScheduler();
    }
    return 0;
}


— создаём семафор (подробнее о нём – ниже). запускаем 3 задачи с разным приоритетом. vLEDTask() у нас будет с самым низким приоритетом – перемигиваться светодиодом («я живой, не завис!») и выводить текущее системное время на экран.
vGPSTask() приоритетом повыше, после запуска банально… засыпает, ожидая получения семафора.
prvSetupHardware() делает следующее:

static void prvSetupHardware( void ){
    SysTick_CLKSourceConfig( SysTick_CLKSource_HCLK );// Тактируемся от HCLK
    NVIC_Configuration();//Настраиваем контроллер прерываний
    GPIO_Configuration();//Порты в/в, к которым подключены светодиоды
    USART2_9600(); //UART2 подключен к GPS EB-500, 9600бит/с
    RTC_Init();// настраиваем часы реального времени
    LCD_Initializtion();//Инициализация
    LCD_Clear(Blue);//Фоновый цвет экрана - синий
}


Вначале задаём HCLK для тактирования ядра, настраиваем контроллер прерываний NVIC (нам нужно включить прерывание при получении данных на USART2), выводы GPIO, USART2 (к которому подключён GPS-приёмник) на 9600 бод, 1 стоп-бит, без четности, 8бит, и последней строчкой – RTC_Init() – включаём встроенную RTC периферию – для отображения текущего времени и даты. А если ещё и батарейку на плату воткнуть, то появится реальная возможность не терять время при отключении питания. :)

Обработчики прерывания задаются в файле stm32f10x_it.c под тем названием, в котором они обозначены в ассемблерном startup_*.s – файле. Мне понадобился только один обработчик – прерывания по приёму байтов с USART2 от gps-приемника:

void USART2_IRQHandler(void)
{
    static portBASE_TYPE xHigherPriorityTaskWoken;
    if (USART_GetITStatus(USART2, USART_IT_RXNE) != RESET){// Если прерывание – по приёму байта
       IrqBuf[IrqBuf_idx++]=USART_ReceiveData(USART2);// Получаем символ от EB-500
       if (IrqBuf_idx==BUFSIZE){// Буфер полон
          IrqBuf_idx=0;
       }
       else if (IrqBuf[IrqBuf_idx-1]==0x0A){// Конец строки
          IrqBuf[IrqBuf_idx]=0x00;
          IrqBuf_idx=0;
          xHigherPriorityTaskWoken = pdFALSE;
          xSemaphoreGiveFromISR( xBinarySemaphore, &xHigherPriorityTaskWoken );
       }
       USART_ClearITPendingBit(USART2, USART_IT_RXNE);// Снимаем флаг прерывания
    }
}


— В обработчике прерывания осуществляем проверку на конец строки. Если нашли символ перевода строки 0x0A, значит можно парсить строку с NMEA данными от EB-500. Непосредственно в обработчике прерывания осуществлять всю обработку идеологически неверно, а иногда и просто опасно – можно не успеть до возникновения следующего прерывания. Обработку вынес в отдельную задачу с наивысшим приоритетом – vGPSTask(), но задача «спит», ожидая получения семафора. А за его выдачу как раз и отвечает xSemaphoreGiveFromISR() в обработчике прерывания. Выставляем семафор, сбрасываем флаг прерывания, и выходим из обработчика – тут же управление передается vGPSTask(), даже если до возникновения прерывания отрабатывалась другая задача, т.к. эта получает право на выполнение благодаря получению семафора.


Начинается обработка данных NMEA, а именно, нас интересуют следующие строки от EB-500:


$GPRMC,161229.487,A,3723.2475,N,12158.3416,W,0.13,309.62,120598,,*10
$GPVTG,309.62,T,,M,0.13,N,0.2,K*6E
$GPGGA,161229.487,3723.2475,N,12158.3416,W,1,07,1.0,9.0,M,,,,0000*18



— каждая такая посылка заканчивается контрольной суммой (*10,*6Е,*18) и символами перевода строки (0x0D, 0x0A). Все данные считаются актуальными только тогда, когда в $GPRMC стоит «А» (как в примере выше). В противном случае получаем «V». При «V» в нашем примере на экран выводится «no signal». Далее парсим и выводим на экран данные:
* из посылки $GPRMC выдёргиваем текущее время в формате hhmmss.sss (в примере выше – это 161229.487)
* из посылки $GPVTG получаем текущую скорость в километрах в час (0,2, К – 0,2 километра в час)
* из посылки $GPGGA мы получаем географическую долготу и ширину в формате ddmm.mmmm / dddmm.mmmm (в примере широта — 3723.2475, долгота — 12158.3416)

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


Эксплуатация системы в моёй машине дала положительные результаты. Даже если завожу машину на холодную – проблем с питанием платы не возникает (привет понижающему dc-dc преобразователю на базе MC34063). Холодный запуск eb-500 составил 20, а горячий – 0.5 минут. Как и ожидалось, при стоянке возникает дрейф координат – и соответственно скорости до 0.5, а иногда и до 7 км/ч. Для мобильных устройств этим можно пренебречь, но для самодельной спутниковой сигнализации на базе gsm-модуля sim900, над которой работаю, это является ощутимой проблемой. Позитивный момент – только благодаря этому спидометру я узнал, что штатный спидометр моей машины спешит на 5км/ч.


Теперь – самая пора фотосессии самодельного спутникового спидометра:





— 16:45:03 на экране – это время UTC. Чтобы перевести на Московское, нужно добавить 4 часа (т.к. переход на зимнее время у нас отменён). Т.е. реально этот снимок я сделал вчера в 19:45. Географические координаты – в часовых координатах (чч.мм.сс.мс). т.е., в первой строке координат, это 55 часов, 48 минут, 22 секунды и 830 миллисекунд. Чтобы найти это место, к примеру, через яндекс карты, переводим координаты в требуемый формат: 55.48.22.830 = 55+48/60+22/3600+830/3600000 =~ 55,806342 (округляем до шестого знака после запятой). Для географической ширины тоже самое: 37.24.22.51 = 37+24/60+22/3600+510/3600000 =~ 37,406253. Забиваем получившиеся значение — 37.406253,55.806342 — в строку поиска Яндекс.Карт и получаем результат: maps.yandex.ru/?text=37.406253%2C55.806342&sll=&sspn=&z=&source=form


Используя графическую библиотеку uC/GUI, можно развить дальше этот пример. Вывести графический спидометр (а заодно, подключившись к катушке зажигания через резистивный делитель к АЦП stm32, и тахометр – получится эдакая цифровая приборная панель автомобиля):



А можно сделать собственный навигатор (да, не просто, но нет ничего невозможного):


В библиотеке GLCD в архиве уже есть файлик USER/GLCD/GLCD_UCGUI.c, являющийся интерфейсом для подключения uC/GUI к этому и некоторым другим графическим экранам. Скачать в инете саму uC/GUI думаю не сложно. Так что перед вами весь необходимый инструментарий, чтобы развить этот пример во что-то куда более полезное.


В следующей статье речь пойдёт о дальнейшем развитии данной работы, для автоматизации автомобилей и домов – систему, объединяющую в себе концепции «Умный дом», «Умный автомобиль», и включающую систему спасения eCall / ЭРА-Глонасс.

Приведённый в статье проект под Keil можно скачать здесь.
Теги:
Хабы:
Всего голосов 23: ↑22 и ↓1+21
Комментарии18

Публикации

Истории

Ближайшие события

15 – 16 ноября
IT-конференция Merge Skolkovo
Москва
22 – 24 ноября
Хакатон «AgroCode Hack Genetics'24»
Онлайн
28 ноября
Конференция «TechRec: ITHR CAMPUS»
МоскваОнлайн
25 – 26 апреля
IT-конференция Merge Tatarstan 2025
Казань