А собственно в чем проблемам? stm32vldiscovery лежала на полке и дожидалась своего часа, программирование знаю и частенько пишу “на заказ”. С железом дружу хорошо.
Первым делом возник вопрос “на чем писать”? Сред программирования много, но язык только “Си”. Без вариантов. Ассемблер не рассматриваю в принципе. Светодиодом помигать можно, но что-то сложнее требует огромных трудозатрат.
Но я не знаю Си! Вообще. Всю жизнь писал только на Pascal/Delphi. Учить язык? Вы пробовали учить язык, когда вам более 40 лет возраста? Когда работа, семья и минимум свободного времени. Когда ум уже не так остр, как в молодости. Да и затевать все это ради одного проект смысла не более, чем учиться на права и покупать машину ради поездки в булочную в соседнем доме.
Выходом послужил найденный “mikroPascal PRO for ARM” от MikroElektronika. Если честно, я уже работал с “mikroPascal PRO for PIC” на пике популярности PIC чипов. Впечатления остались не очень хорошие. Компилятор “со странностями”, оболочка тоже не отличалась стабильностью и дружественным интерфейсом.
Тем более интересно было посмотреть, что изменилось за эти годы и в какую сторону.
И так, что мы имеем на руках:
- Плату stm32f4discovery;
- mikroPascal PRO for ARM с лицензионном ключем (взято у товарища. потом придется вернуть). Без ключа — ограничение в 2 КВ на размер кода;
- Инженер, которого в ВУЗе учили исключительно Pascal.
Задача: освоить программирование микроконтроллера без единой строчки Си кода.
Итак приступим…
Создание проекта несложно. File -> New — > New Project.
Указать тип микроконтроллера. Спросит, какие стандартные библиотеки используем ( по умолчанию — все). Оставляем. “Лишние” библиотеки при компиляции будут выкинуты автоматом.
Не забываем настроить тактиирование. Если есть сомнения — воспользуетесь одним из стандартных “Scheme”. Это набор настроек для параметров тактирования.
Первым делом поиграемся светодиодами. Давайте просто попросим их гореть. Напоминаю, светодиоды сидят на портах D12, D13, D14 и D15.
program MyProject1; begin // инициализируем порт на выход GPIO_Digital_Output(@GPIOD_BASE, _GPIO_PINMASK_12 or _GPIO_PINMASK_13 or _GPIO_PINMASK_14 or _GPIO_PINMASK_15); // зажигаем SetBit(GPIOD_ODR, 12); SetBit(GPIOD_ODR, 13); SetBit(GPIOD_ODR, 14); SetBit(GPIOD_ODR, 15); while true do nop; end.
Стоп! Сейчас у огромного количества народа, имеющего опыт работы с данными микроконтроллерами, возникнет вопрос: а где включение тактирования порта?!
Очень просто: при иниациализавции порта на ввод или вывод тактирование включается автоматически. Не верите — идем View->Statistics->Functions Tree (моя любимая!).
Если есть сомнения в том, что компилятор делает “автоматом” — идем и смотрим. Смотреть на горящий светодиод конечно приятно. Но скучно. Давайте попросим его помигать
program MyProject1; begin // выставляем пины на выход и вход GPIO_Digital_Output(@GPIOD_BASE, _GPIO_PINMASK_12 or _GPIO_PINMASK_13 or _GPIO_PINMASK_14 or _GPIO_PINMASK_15); while true do begin if TestBit(GPIOD_ODR, 12) then ClearBit(GPIOD_ODR, 12) else SetBit(GPIOD_ODR, 12); Delay_1sec; end; end.
На плате есть кнопка. Давайте и её запустим в работу. При нажатии на кнопку светодиоды загораются. А при отпускании — гаснут (правда неожиданно! — шутка). Кнопка у нас на A0. Для чтения используем функцию Button. Она может подавлять дребезг контактов и учитывает логику кнопки ( НЗ или НО). В принципе можно легко читать состояние бита порта «напрямую», но так читабельнее.
program MyProject1; begin // выставляем пины на выход и вход GPIO_Digital_Output(@GPIOD_BASE, _GPIO_PINMASK_12 or _GPIO_PINMASK_13 or _GPIO_PINMASK_14 or _GPIO_PINMASK_15); GPIO_Digital_Input(@GPIOA_BASE, _GPIO_PINMASK_0); while true do if Button(GPIOA_IDR, 0, 10, 1) then begin // зажигаем SetBit(GPIOD_ODR, 12); SetBit(GPIOD_ODR, 13); SetBit(GPIOD_ODR, 14); SetBit(GPIOD_ODR, 15); end else begin // гасим ClearBit(GPIOD_ODR, 12); ClearBit(GPIOD_ODR, 13); ClearBit(GPIOD_ODR, 14); ClearBit(GPIOD_ODR, 15); end; end.
Прямой опрос кнопки в цикле? Не всегда есть возможность постоянно проверять кнопку. Часто кристалл занят более важными делами, чем опрос кнопок. И мы можем просто “упустить” момент нажатия. Правильнее будет при нажатии кнопки генерировать прерывание, где и обрабатываем событие.
program MyProject1; procedure INT_EXTI0(); iv IVT_INT_EXTI0; ics ICS_AUTO; begin EXTI_PR:=$FFFF; // clear flag if TestBit(GPIOD_ODR, 12) then ClearBit(GPIOD_ODR, 12) else SetBit(GPIOD_ODR, 12); end; begin // настраиваем выводы GPIO_Digital_Output(@GPIOD_BASE, _GPIO_PINMASK_12); GPIO_Digital_Input(@GPIOA_BASE, _GPIO_PINMASK_0); EXTI_IMR := %1; Разрешаем прерывание от нужного входа EXTI_FTSR :=$FFFF; Прерывание по приходу 0 NVIC_IntEnable(IVT_INT_EXTI0); // Enable External interrupt EnableInterrupts(); while true do ; end.
Что у нас простаивает в кристалле? Таймер? Давайте с режимом ШИМ поиграемся. Что там по выводам? 4-й таймер выходит на вывод, что подсоединен к светодиоду. Так что мы ждем?
program MyProject1; var ratio, tmp: word; begin ratio := PWM_TIM4_Init(25000); tmp:=0; PWM_TIM4_Set_Duty(0, _PWM_NON_INVERTED, _PWM_CHANNEL1); PWM_TIM4_Start(_PWM_CHANNEL1, @_GPIO_MODULE_TIM4_CH1_PD12); while true do begin PWM_TIM4_Set_Duty(ratio-tmp, _PWM_INVERTED, _PWM_CHANNEL1); Inc(tmp); if tmp>ratio then tmp:=0; Delay_1ms; end; end.
Таймер можно использовать не только для ШИМ. Руки просто чешутся использовать таймер для выполнения периодических действий. Давайте например помигаем таймером. Таймер будет дергать прерывание, а сам прерывание — управлять светодиодом.
Для этого нужно долго и упорно считать коэффициенты для записи в регистры таймера. А если нет — воспользоваться бесплатной программой “Timer Calculator” с сайта производителя.
Вбиваем исходные данные и получаем готовый код таймера. Как-то так:
program ETXI; procedure Timer2_interrupt(); iv IVT_INT_TIM2; begin TIM2_SR.UIF := 0; if TestBit(GPIOD_ODR, 12) then ClearBit(GPIOD_ODR, 12) else SetBit(GPIOD_ODR, 12); end; procedure InitTimer2(); begin RCC_APB1ENR.TIM2EN := 1; TIM2_CR1.CEN := 0; TIM2_PSC := 2239; TIM2_ARR := 62499; NVIC_IntEnable(IVT_INT_TIM2); TIM2_DIER.UIE := 1; TIM2_CR1.CEN := 1; end; begin // выставляем пины на выход GPIO_Digital_Output(@GPIOD_BASE, _GPIO_PINMASK_12); // Enable digital output on PORTD // //Timer2 Prescaler :2239; Preload = 62499; Actual Interrupt Time = 1 InitTimer2(); while(TRUE) do nop; // Infinite loop end.
UART? Тоже легко. Подключаем переходник UART-USB. Давайте для примера выводить значение температуры процессора каждую секунду.
program MyProject1; var uart_tx : string[20]; tmp: integer; tmp1:real; begin UART2_Init(9600); // Настраиваем UART на скорость 9600 bps ADC1_Init(); Delay_ms(100); // Ожидаем стабилизации UART TSVREFE_bit:=1; UART2_Write_Text('Hello!'); UART2_Write(13); UART2_Write(10); while (TRUE) do begin tmp:= ADC1_Read(16); // Читаем данные температуры tmp1:=(3300*tmp)/4095; // Пересчитываем mV. Из расчета: 3.3 V = 4096 aene?ao tmp1:= ((tmp1-760)/2.5)+25; // Считаем в градусах. V25=0.76V S=2.5mV/C FloatToStr(tmp1, uart_tx); uart_tx:='T: '+uart_tx+ ' C'; UART2_Write_Text(uart_tx); UART2_Write(13); UART2_Write(10); Delay_ms(1000); end; end.
Запускаем… Температура корпуса -27С. Это при том, что в комнате 23С. В принципе ничего странного. Сам производитель не рекомендует использовать данный датчик для измерения абсолютных температур, а использовать только для измерения роста/уменьшения температуры. Выходное напряжение датчика температуры сдвинуто от чипа к чипу, и сдвиг может достигать 45 градусов.
Стоп! Мы забыли сделать инициализацию GPIO? А тут еще одна “фирменная” фишка: при инициализации модуля, работающего “на выводы”, инициализация выводов происходит автоматически. Сомнения? Идем View->Statistics->Functions Tree (моя любимая!).
Как видим выводы автоматом переведены в альтернативный режим и для них включено тактирование.
И напоследок — доберемся до USB. Самое простое. Сделаем HID устройство, которое просто возвращает принятое сообщение.
program HID_Read_Write_Polling; var cnt, kk : byte; var readbuff : array[64] of byte; var writebuff : array[64] of byte; begin HID_Enable(@readbuff,@writebuff); while TRUE do begin USB_Polling_Proc(); // Call this routine periodically kk := HID_Read(); if (kk <> 0) then begin for cnt:=0 to 63 do writebuff[cnt]:=readbuff[cnt]; HID_Write(@writebuff,64); end ; end; end.
Или аналогичное действие с использованием Interrupt:
program HID_Read_Write; var cnt : byte; var readbuff : array[64] of byte; var writebuff : array[64] of byte; procedure USB1Interrupt(); iv IVT_INT_OTG_FS; begin USB_Interrupt_Proc(); end; begin HID_Enable(@readbuff,@writebuff); while TRUE do begin while(HID_Read() = 0) do ; for cnt:=0 to 63 do writebuff[cnt] := readbuff[cnt]; while(HID_Write(@writebuff,64) = 0) do ; end; end.
Для нормальной работы этой программы (и любой программы, в которой используется стандартная библиотека USB) необходимо сгенерировать файл USBdsc.mpas. В нем прописана параметры USB для данного устройства. Сразу скажу, с меню “Tools” присутствует утилита “HID Terminel”. Эта утилита позволяет сформировать правильный файл, достаточный для запуска примеров и простых приложений. А если что посложнее — смотрим описание шины USB. И правим файл. Благо он щедро снабжен комментариями.
При помощи этой утилиты проверяем работу примера:
Конечно сейчас будут куча ворчания насчет “неэффективности” кода. Но посмотрите — достаточно “тяжелый” пример с USB HID занимает 1.7 % флеша и 1.2% опреативной памяти.
И небольшое отступление.
Вся эта “волшебная” машинерия работает только при правильной настройке системы тактирования. Если абсолютно правильный код начинает “чудить”, внешние интерфейсы начинают “пропадать” а подключение USB интерфейса порождает сообщения “Ошибка запроса идентификатора устройства” — откройте настройки проекта и еще раз проверьте тактирование.
Все это показывает, что знание Си не является обязательным условием для эффективного программирования микроконтроллера. А наличие стандартных библиотек значительно снижают порог вхождения для неподготовленных людей. При этом код получается достаточно компактный и читабельный.
Подробное описание среды программирования — будет написано отдельной статьей.
P.S.: На самом деле я знаю Си (С++). Свободно читаю и понимаю код. Но написание программ на данном языке является для меня “некомфортным”. Мозги автоматом выдают выход в паскалеподобном коде.
Комментарии (20)
dkukushkin
10.08.2015 12:11+2Но я не знаю Си! Вообще. Всю жизнь писал только на Pascal/Delphi. Учить язык? Вы пробовали учить язык когда вам более 40 лет возраста?
Ни прибедняйтесь! После Pascal на C вы перейдете без проблем и в 60. Ведь речь не о Lisp и не о Haskell.
Причем это сделать полезно — меньше риск развития слабоумия в старости.
segrus
10.08.2015 12:35+5Что вы накинулись на человека со своим «Си»? Он просто дал иной способ написания кода. И сделал это блестяще!
Автору спасибо за интересную статью и ждём продолжения!MacIn
10.08.2015 13:25+3Поддерживаю. Перед автором стоит инженерная задача, для решения которой у него есть ресурсы, в том числе время. При наличии знакомого инструмента, который позволит решить ту же задачу, тратить время на изучение неизвестного для разовой работы просто потому что так принято — нерационально.
Keroro
10.08.2015 13:14Помнится, был еще Бейсик, от этой же конторы…
segrus
10.08.2015 13:27Он и щас есть (в моём случае для AVR). С удовольствием пишу в BASCOM и не нарадуюсь, что он ещё развивается.
Хотя знаю и «Cи». Но часто, например, проще, быстрее и приятнее написать 2 строчки в Basic, чем кучу непонятного кода в «Си», а потом ловить пол дня ошибки.
safari2012
10.08.2015 13:44+1Что-то код в листингах плохо скопировался/вставился — всё в одну строку.
Неужели нельзя было проверить/поправить перед постингом статьи?DanilinS
10.08.2015 23:28Вы не поверите — тщательно «вылизал» и проверил. Но это моя первая статья. Пока статью не одобрят — я её не вижу. При редактировании — все красиво. А при публикации совершенно неожиданно вылезла эта проблема.
Спасибо за сообщение — подправил страницу для правильного отображения кода.
segrus
11.08.2015 07:25Ещё раз спасибо за статью! Открыл для себя MikroBasic ARM. Улётная вещь! Прощайте сотни строк ST-шного пестрящего кода! Теперь я могу думать над идеей устройства, а не ломать голову «Си» и сотней глючных h-файлов. Таким и должен быть язык программирования.
mmMike
Уж извините. Сочетание «С железом дружу хорошо.» и «Но я не знаю Си! Вообще. Всю жизнь писал только на Pascal/Delphi.»
Сразу вызывает когнитивный диссонанс.
Программирование микроконтроллеров и «С/С++» — «синонимы» уже давно.
Статья заинтересовала только как «удаление гланд через анальное отверстие».
MacIn
С каких пор «дружу с железом» — всенепременно программирование микроконтроллеров?
mmMike
В статье ни слова о другом контексте жаргонизма «железо» (аналоговые/цифровые схемы и пр.).
Только контекст микроконтроллеров.
MacIn
Учитывая, что человек говорит о МК как об «одном проекте», это что-то иное. Вот, мой пример: увлекался радиолюбительством, собирал множество аналоговых устройств. Скоро собираюсь собрать и наладить радиолюбительский Радио86-РК. Для этого тоже надо «дружить с железом». но там Си и близко нет — голый Intel 8080 ассемблер.
mmMike
> Скоро собираюсь собрать и наладить радиолюбительский Радио86-РК.
В 89 году, использовал кросс С (MS DOS) для сборки программ на Z80.
Кажется там был вариант и для 8080 набора команд.
При наличие доступных корпусов Z80, использовать более дорогие (на тот момент) 8080 необходимости не было.
Но… простите, в 2015 году собирать что то на 8080. Хотя и писать на ассемблере для него как раз в русле темы «собрать и наладить радиолюбительский Радио86-РК».
Можно еще и ламповый телевизор спаять по схеме из журнала Радио…
Не осуждаю и не критикую, просто не понимаю.
MacIn
Да, у меня есть этот компилятор. Но это «кросс», это «не честно». А целиком в 32 Кб ОЗУ он не поместится.
Зато 580ВМ80А — под рукой.
Люблю ретро. Это просто разминка, потом буду собирать следующий ПК собственной конструкции, для т.н. demo.
Это просто был пример «железа».
Sykoku
«Удаление гланд» — это из личного опыта?
Не обижайтесь, но есть вещи, где С (в любой его реинкарнации) вызывает, как уже говорил кто-то ниже, бешеную потерю времени на отладку. У Вас оно есть — я рад. Высказывание Шерлока Холмса помните? Ему было все равно, Земля вращается вокруг Солнца или наоборот. Для его работы это знание бесполезно. На его чердаке не свалена куча бесполезного хлама (пару версий компиляторов или библиотек, зачастую не работающие при смене версии языка или окружения), а есть только набор всего необходимого, но в безукоризненном порядке.
И насчет слабоумия.
Паскаль — это ящик с инструментами, где все на своих местах.
Си — коробка с размотанными шпульками с нитками. Если потянешь не за ту (а зачастую код пишут не для себя и не ты его потом сопровождаешь -или Вы всю жизнь проведете на одном месте?) — получишь кашу на те самые полдня работы. В лучшем случае.
И не все железо — это С. Есть вещи, где либо ассемблер (я их знаю 8), либо еще попроще (переключатели).
mmMike
Если бы я не сопровождал и не разрабатывал практически каждый день на работе (а данное время):
1. C (ПО платежных терминалов, которое лень/нет смысла на C++ переводить)
2. C++ (сервисы Windows + сервисы Unix + FM модуль SafeNet HSM + АРМ curses Unix)
3. PL-SQL функционал для Unix сервисов и Web.
4. python для скриптов
5. Java (Swing приложения + Web (SOAP) сервисы + Web gwt + JavaCard)
и не работал раньше с lisp (Autocad), fortran, perl, v.basic и Pascal (Delphi) довольно долго, то может и захотел бы продолжить этот спор.
А так — какая мне разница. Пишите на чем хотите. Меня забавляют выступления апологетов конкретных языков программирования.
Лично мне все равно какой язык использовать. Принципы везде одинаковы. Программирование это не знание конкретного языка…
А данном же контексте — тратить время на поиск экзотичного компилятора вместо того что бы потратить час на то что бы глянуть на синтаксис С!
Все же С++ не так уж от Object Pascal отличается, а C вообще прост.
Для ARM (STM32) вообще C++ в принципе не использую. Не так уж там много ОЗУ, что бы на динамическую память тратить.
Область Pascal==Delphi ограничена исключительно тем, на что мертвый Borland обращал внимание. Отсюда и порядок. Как на кладбище!
P.S. насчет слабоумия — это вы мой пост с каким то другим перепутали…
Sykoku
Вы меня то же с кем-то перепутали. Я не заставляю писать на чем-то одном.
Мое «детство» в программировании началось с блок-схем. Потом ушло в Бейсик, в классе 8-м (BBС-ий больше всего нравился, хотя выучил штук 6 диалектов — благо литература была, хоть и при СССР). Да и матчасть (ДВК-1/2 и Искра-1030 позже) не особо позволяла. А вот на низком уровне писать приходилось чаще — ЕС/СМ. Потом в институте пригодилось — старый плоттер подключили к 286-й — и чертежи на диплом не надо рисовать.
Потом были PDP и НР, micro VAX (создание терминала).
Довелось работать и на аналоговых машинах (вот вам настоящая ОС РВ).
Кассовые аппараты, терминалы — только ASM. Иначе потом в коде получаются пустоты и многочисленные лишние телодвижения на сотню-другую тактов (в лучшем случае), а в некоторых сферах это не применимо.
Писал на С, Prolog'e, Яве, Графорте. Баловался Модулой и Адой.
И поэтому я не ратую ни за один из языков. И тех, что описал, и тех, на которых сейчас пишу/сопровождаю. У каждого — своя ниша.