Програмування ARM на Pascal

Одного разу, раптом абсолютно несподівано і без оголошення війни, з'явилася ідея. І потрібно для цього написати і запрограмувати кристал STM32.

А власне в чому проблем? stm32vldiscovery лежала на полиці і чекала своєї години, програмування знаю і частенько пишу «на замовлення». З залізом дружу добре.

Першим ділом виникло питання «на чому писати»? Середовищ програмування багато, але мову лише «Сі». Без варіантів. Асемблер не розглядаю в принципі. Світлодіодом помигати можна, але щось складніше вимагає величезних трудовитрат.

Але я не знаю Сі! Взагалі. Все життя писав тільки на Pascal/Delphi. Вчити мову? Ви пробували вчити мову коли вам більше 40 років віку? Коли робота, сім'я і мінімум вільного часу. Коли розум вже не такий гострий, як у молодості. Та й затівати все це заради одного проект сенсу не більше, ніж вчитися на права і купувати машину заради поїздки в булочну в сусідньому будинку.

Виходом послужив знайдений «mikroPascal PRO for ARM» від MikroElektronika. Якщо чесно, я вже працював з «mikroPascal PRO для 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); // External interrupt Enable
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 äèñêðåò
tmp1:=((tmp1-760)/2.5)+25; // Вважаємо в градусах. V25=0.76 V S=2.5 mV/C
FloatToStr(tmp1, uart_tx);
uart_tx:=': '+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 do 63
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 do 63
writebuff[cnt] := readbuff[cnt];
while(HID_Write(@writebuff,64) = 0) do
;
end;
end.


Для нормальної роботи цієї програми (і будь-якої програми, в якій використовується стандартна бібліотека USB) необхідно згенерувати файл USBdsc.мпа. В ньому прописана параметри USB для даного пристрою. Відразу скажу, з меню «Tools» присутня утиліта «HID Terminel». Ця утиліта дозволяє сформувати правильний файл, достатній для запуску прикладів і простих додатків. А якщо що складніше — дивимося опис шини USB. І правимо файл. Благо він щедро забезпечений коментарями.



За допомогою цієї утиліти перевіряємо роботу прикладу:



Звичайно зараз буде купа бурчання щодо «неефективності» коду. Але подивіться — досить «важкий» приклад з USB HID займає 1.7 % флеша і 1.2% опреативной пам'яті.



І невеликий відступ.

Вся ця «чарівна» машинерія працює тільки при правильному налаштуванні системи тактирования. Якщо абсолютно правильний код починає «чудити», зовнішні інтерфейси починають «випадати» а підключення USB інтерфейсу породжує повідомлення «Помилка запиту ідентифікатора пристрою» — відкрийте налаштування проекту і ще раз перевірте тактування.



Все це показує, що знання Сі не є обов'язковою умовою для ефективного програмування мікроконтролера. А наявність стандартних бібліотек значно знижують поріг входження для непідготовлених людей. При цьому код виходить досить компактний і зручний для читання.

Детальний опис середовища програмування — буде написано окремою статтею.

P. S.: насправді я знаю Сі (С++). Вільно читаю і розумію код. Але написання програм на даному мовою є для мене «некомфортним». Мізки автоматом видають вихід в паскалеподобном коді.

Джерело: Хабрахабр

0 коментарів

Тільки зареєстровані та авторизовані користувачі можуть залишати коментарі.