Инструменты пользователя

Инструменты сайта


ob:o7:first

Это старая версия документа!


Первая программа на языке программирования Оберон

В предыдущей заметке было описано, как создать новый проект в среде разработки Рекордино, компилировать модуль-заготовку и затем — сформировать файл прошивки и записать его в память микроконтроллера.

Теперь разберём детально сам код этого небольшого модуля, чтобы лучше понять идеологию модульного подхода и особенности языка программирования Оберон.

После запуска проекта открывается следующий код:

    MODULE Main;
    
    
    IMPORT SYSTEM, 
        ARMv7M := MicroARMv7M, 
        SysTick0 := MobxARMv7MSTM32SysTick0, 
        MCU := MicroSTM32F4, 
        Pins := MicroSTM32F4Pins, 
        Sys := MicroSTM32F4System;


    VAR
        msec: INTEGER;


    PROCEDURE Setup;
    BEGIN
        msec := 0;
        SysTick0.Init(Sys.HCLK, 1000);
        Pins.Configure(Pins.C, 13,
                   Pins.output, Pins.pushPull, Pins.medium, Pins.noPull, Pins.AF0);
    END Setup;
    
    PROCEDURE Loop;
    BEGIN
        REPEAT
            IF SysTick0.OnTimer() THEN
                IF msec = 999 THEN msec := 0 ELSE INC(msec) END;
                IF msec = 0 THEN
                    SYSTEM.PUT(MCU.GPIOCBSRR, {13})
                ELSIF msec = 500 THEN
                    SYSTEM.PUT(MCU.GPIOCBSRR, {13 + 16})
                END;
            END;
            ARMv7M.WFI
        UNTIL FALSE
    END Loop;


BEGIN
    Setup;
    Loop
    
END Main. 

Программа прошивки состоит из модулей, как из кирпичиков Лего.

Модуль всегда начинается с слова MODULE, после которого следует его название. И заканчивается словом END, после которого повторяется название, и завершается точкой.


MODULE Main;

END Main.
    

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

Секция импорта начинается ключевым словом IMPORT, после которого через запятую идут названия других модулей — строительных блоков программы. При этом с помощью оператора присваивания := для модулей допускается указывать короткие обозначения.


MODULE Main;
 
   IMPORT SYSTEM, 
       ARMv7M := MicroARMv7M, (* общее для всех контроллеров архитектуры ARMv7 *)
       SysTick0 := MobxARMv7MSTM32SysTick0, (* пример таймера на прерываниях *)
       MCU := MicroSTM32F4, (* ардеса регистров для микроконтроллеров STM32F4* *)
       Pins := MicroSTM32F4Pins, (* модуль для настройки портов ввода/вывода *)
       Sys := MicroSTM32F4System; (* модуль для базовой настройки STM32F4* *)
   
   

После секции импорта возможно указывать константы и описывать новые типы данных. Однако в этой простой программе это не используются, поэтому за секцией импорта следует секция глобальных переменных VAR. После идентификатора (имени) переменной через двоеточие указывается тип данных, который будет хранить переменная.

VAR
    msec: INTEGER;
   

В Обероне существует шесть базовых типов для переменных:

MODULE Main;
    
    IMPORT SYSTEM, 
            ARMv7M := MicroARMv7M, (* общее для всех контроллеров архитектуры ARMv7 *)
            SysTick0 := MobxARMv7MSTM32SysTick0, (* пример таймера на прерываниях *)
            MCU := MicroSTM32F4, (* ардеса регистров для микроконтроллеров STM32F4* *)
            Pins := MicroSTM32F4Pins, (* модуль для настройки портов ввода/вывода *)
            Sys := MicroSTM32F4System; (* модуль для базовой настройки STM32F4* *)
        

После описания переменных идёт описание процедур и программный код модуля. В этом примере две процедуры:

Setup — отвечает за настройку микроконтроллера,

Loop — содержит главную петлю программы, которая повторяется во время работы микроконтроллера после настройки.

MODULE Main;
...

    PROCEDURE Setup;
    BEGIN
        ...
    END Setup;
    
    PROCEDURE Loop;
    BEGIN
        ...
    END Loop;

BEGIN
    Setup;
    Loop
END Main. 
    

Процедура настройки Setup имеет три выражения:

  1. установка начального значения глобальной переменной;
  2. настройка частоты таймера делается вызовом процедуры *Init* из модуля SysTick0. Процедура имеет два аргумента, мы передаём ей частоту работы главного кварца микроконтроллера и желаемую частоту вызова таймера в Герцах (так таймер будет вызываться 1000 раз в секунду);
  3. настройка вывода PC13 осуществляется с помощью специального модуля Pins через процедуру Configure, 6 параметров задают порт, номер вывода, тип вывода (вход/выход), наличие/отсутствие подтяжки, скорость работы, тип подтяжки, и вариант альтернативной функции для вывода. (если ножка будет работать в режиме GPIO, то нужно использовать значение Pins.AF0).
PROCEDURE Setup;
BEGIN
  msec := 0;
  SysTick0.Init(Sys.HCLK, 1000);
  Pins.Configure(Pins.C,13,Pins.output,Pins.pushPull,Pins.medium,Pins.noPull,Pins.AF0);
END Setup;

Процедура Loop содержит бесконечный цикл REPEAT UNTIL FALSE, в котором находится два выражения:

  1. условие проверки срабатывание таймера SysTick0.OnTimer() с кодом, который выполняется, если таймер сработал;
  2. команда которая отправляет микроконтроллер в спящий режим до срабатывания следующего вызова таймера или иного прерывания

ARMv7M.WFI

PROCEDURE Loop;
    BEGIN
        REPEAT
            IF SysTick0.OnTimer() THEN
                IF msec = 999 THEN msec := 0 ELSE INC(msec) END;
                IF msec = 0 THEN
                    SYSTEM.PUT(MCU.GPIOCBSRR, {13})
                ELSIF msec = 500 THEN
                    SYSTEM.PUT(MCU.GPIOCBSRR, {13 + 16})
                END;
            END;
            ARMv7M.WFI
        UNTIL FALSE
    END Loop;

Внутри условия, есть выражение для добавления единицы к счётчику милисекунд INC(msec), а также условия сброса счётчика в 0, когда его значение достигнет константы 999:

IF msec = 999 THEN msec := 0 ELSE INC(msec) END;

Второе условное ветвление, в зависимости от значения глобальной переменной msec с помощью команды SYSTEM.PUT устанавливает значение битов регистра GPIOC_BSRR , так что сначала на вывод PC13 подаётся напряжение 3,3 Вольта, а затем через половину секунды напряжение устанавливается в 0 Вольт.

IF msec = 0 THEN
    SYSTEM.PUT(MCU.GPIOCBSRR, {13})
ELSIF msec = 500 THEN
    SYSTEM.PUT(MCU.GPIOCBSRR, {13 + 16})
END;

{13} ­— это константа типа SET, которая соответствует двоичному числу с единицей в положении 13.

0000 0000 0000 0000 0001 0000 0000 0000

Запись такого машинного слова по адресу MCU.GPIOCBSRR вызывает документированные изменения напряжений на выводе PC13, как описано выше.

{13+16} ­— это также константа типа SET, которая соответствует двоичному числу с единицей в положении 29.

0001 0000 0000 0000 0000 0000 0000 0000

Мы специально записали эту константу в виде суммы 13+16, чтобы было легче читать код. Ведь запись такого машинного слова по адресу MCU.GPIOCBSRR вызывает установку нуля на ножке PC13.

«Почему один регистр управляет и включением и выключением напряжения?» — вероятно спросите вы… Это очень удобно, так как позволяет за одну операцию сразу включить и выключить группы выводов.

К примеру вот такая команда за один такт работы микроконтроллера включит напряжение на выводах PC1 и PC3, при этом выключит напряжения начиная с PC6 и до PC12.

SYSTEM.PUT(MCU.GPIOCBSRR, {1,3,6+16..12+16})

BSSR — расшифровывается как Bit Set/Reset Register (регистр для установки и сброса бит).

Подробнее про управление регистрами читайте в заметке в википедии по Оберону.

Управление микроконтроллером — это запись и чтение данных из регистров. Про чтение регистров мы поговорим в следующей заметке, где разберём подключение кнопки к микроконтроллеру.

Задавайте вопросы в группе ВК или в телеграм-чате.

ob/o7/first.1777021161.txt.gz · Последнее изменение: 2026/04/24 11:59 — саша