Самый лучший способ изучить новый язык программирования - Это сразу начать писать на нём программы.

Брайн Керниган и Деннис Ритчи

Традиционно, первой программой, с которой начинают изучение нового языка бывает программа "Hello World".

Первые шаги

Чтобы начать писать программу на языке Си для микроконтроллера RP2040 распаянного на плате Raspberry Pi Pico в Интегрированной Среде Разработки (IDE) Visual Studio Code (VS Code) выберите на левой панели кнопку Raspberry Pi Pico Project.

В панели левого меню  Raspberry Pi Pico Project : Quick Access выберите пункт меню New C/C++ Project. Откроется окно изображённое на рисунке 1.

Рис. 1. Visual Studio Code. Окно New C/C++ Project.

В окне изображённом на Рис. 1 в поле Name введите имя нового проекта. У нас проект будет называться "HelloWorld01".

В выпадающем списке Board Type выберите pico - название платы с микроконтроллером RP2040 фирмы Raspberry Pi.

В поле выбора папки (Location) выберите в своём рабочем каталоге папку в которой будут сохраняться все ваши проекты создаваемые в среде VS Code.

Поставьте галочку в check box Console over USB (disables other USB use). Это необходимо, так как мы собираемся писать программу с выводом текста в консоль компьютера и вывод этот будет происходить по USB интерфейсу.

Теперь, в окне New C/C++ Project можно нажать кнопку Create. Кнопку Create вы найдёте в нижней части окна New C/C++ Project, возможно придётся воспользоваться полоской прокрутки окна.

В панели левого меню откроется Проводник. См. Рис. 2.

Рис. 2. Visual Studio Code. Проводник и программа на Си.

В проводнике выберите программу на языке Си. У нас имя файла с программой совпадает с названием проекта, а расширение файлов с программами на Си состоит из одной буквы "c" после точки. Так что, это файл HelloWorld.c

В рабочем окне программы VS Code откроется файл HelloWorld.c с программой на языке Си для редактирования. Как вы видите, этот файл не пустой, это заготовка программы с возможностью использования вывода текста в консоль компьютера и вывод этот будет происходить по USB интерфейсу. Программа HelloWorld01.c полностью законченный вариант программы для вывода текста  "Hello, World!" в терминал компьютера, если компьютер и плата с микроконтроллером соединена по USB интерфейсу. В этой программе все конструкции языка Си используются корректно, в соответствии с синтаксисом языка, и несмотря на то, что это заготовка для написания своей программы, это полноценная работоспособная программа - пример.

Программа HelloWorld01.c, перед запуском должна быть скомпилирована и загружена в микроконтроллер RP2040 установленный на плате Raspberry Pi Pico. В правом нижнем углу в программе VS Code расположены кнопки Compile и Run.

Выбор кнопки Compile запускает процедуру компиляции программы. В процессе компиляции текст программы на языке C/C++ (файл с расширением c) компилируется в результате чего создаётся объектный файл с расширением obj. Далее, происходит линковка в результате чего создаётся файл с расширением elf. Elf файл конвертируется в файл формата uf2. Файл формата uf2 содержит вашу программу для микроконтроллера RP2040 формате двоичного кода, который микроконтроллер может читать, понимать и выполнять.

Компиляция программы происходит в папке build вашего проекта. Структура файлов в этой папке показана на следующем листинге:

build/
├── CMakeFiles/              # Служебные файлы CMake
│   ├── 3.25.1/              # Версия CMake
│   ├── CMakeTmp/
│   ├── YourProject.dir/      # Файлы вашего проекта
│   │   ├── src/
│   │   │   └── main.c.obj   # Объектный файл
│   │   ├── build.make
│   │   ├── flags.make
│   │   └── ...
├── pico-sdk/                # Локальная копия SDK
│   └── ...
├── generated/               # Сгенерированные файлы
│   └── ...
├── CMakeCache.txt           # Кэш настроек CMake
├── Makefile                 # Главный make-файл
├── YourProject.elf          # Исполняемый файл (ELF)
├── YourProject.uf2          # Прошивка для загрузки
├── YourProject.bin          # Бинарный образ
├── YourProject.hex          # HEX-образ
├── YourProject.dis          # Дизассемблированный код
└── ...

Для программиста, в папке build может представлять интерес файл с расширением dis. В этом файле находится дизассемблированная программа, созданная из двоичного кода и дополненная мнемониками на языке Assembler для вашего микроконтроллера.

Загрузка программы в микроконтроллер и запуск программы на выполнение.

  • Подключите плату Raspberry Pi Pico к компьютеру по USB интерфейсу. При этом следует соблюдать определённый порядок действий:
  • Зажмите и не отпускайте кнопку Boot на плате Raspberry Pi Pico.
  • Подключите плату к компьютеру кабелем USB.
  • Отпустите кнопку Boot.

Если все действия по подключению вы выполнили правильно, компьютер обнаружит подключение нового USB устройства (Flash Card RPI-RP2).

После компиляции программы в VS Code нажмите кнопку Run. Программа будет загружена в микроконтроллер и сразу же запустится. USB Flash карта отключится автоматически.

Мы запустили на выполнение программу "Hello World". " с выводом текста в консоль компьютера и вывод этот будет происходить по USB интерфейсу. Наш микроконтроллер уже подключен к компьютеру USB кабелем, теперь необходимо увидеть вывод текста "Hello World!" на компьютере и убедиться, что программа в микроконтроллере работает.

В VS Code В правом нижнем углу во время компиляции и загрузки программы в микроконтроллер открылся терминал, через который мы могли наблюдать сообщения от процесса компиляции и загрузки. Теперь, выберите в меню этого окна с сообщениями пункт SERIAL MONITOR. См. рис. 3. Если Serial Monitor (Монитор последовательного интерфейса) настроен правильно, вы должны увидеть в окне монитора строки "Hello World" появляющиеся каждую секунду. 

Рис. 3. Visual Studio Code.

Как работает программа

Мы воспользовались примером программы "Hello World", см. рис. 2 и лист. 2, давайте разберёмся как она работает.

Программа на Си, независимо от её размера, состоит из функций и переменных. функция содержит операторы - команды для выполнения определённых вычислительных операций, а в переменных хранятся числа и другие данные, используемые в этих операциях.

#include <stdio.h>
#include "pico/stdlib.h"


int main()
{
    stdio_init_all();

    while (true) {
        printf("Hello, world!\n");
        sleep_ms(1000);
    }
}

Лист. 2. Программа "Hello World"

В нашем примере главная функция имеет имя main. Обычно функциям можно давать любые имена по своему усмотрению, но main - особый случай, поскольку программа начинает выполняться именно с этой функции. Это означает, что в любой программе на языке Си обязательно должна присутствовать функция main.

Обычно из функции main для выполнения разных операций вызываются другие функции, некоторые из них программист пишет сам, а другие содержатся в стандартных библиотеках. В функции main первой вызывается функция stdio_init_all, это функция из библиотеки pico/stdlib.h.

В строке программы #include "pico/stdlib.h" мы указываем компилятору включить в программу информацию о библиотеке pico/stdlib.h.

Один из способов передачи данных между функциями состоит в том, что функция при обращении к другой функции передает ей список значений, называемых аргументами (параметрами). Этот список заключают в круглые скобки и помещают после имени функции. В нашем примере main определена как функция без аргументов, на что указывает пустой список (). Операторы функции заключают в фигурные скобки. Операторы внутри фигурных скобок называют телом функции. Функция main в примере листинг 1 содержит 2 функции, которые будут выполнены по очереди сверху вниз по программе.

Первая функция в теле функции main - это функция stdio_init_all без параметров. Вторая функция в теле функции main - это функция while с параметром true. У функции while есть тело и оно, в свою очередь, содержит 2 функции - printf и sleep_ms. Функция printf содержит в качестве параметра строку "Hello World!\n". Функция sleep_ms имеет в качестве параметра число 1000.

Следует заметить, что функция while с параметром true создаёт бесконечный цикл в котором бесконечно будет выполняться тело цикла.

Подытожим:

  1. при старте программы листинг 2 автоматически запускается функция main без параметров,
  2. в ней, по имени вызывается функция stdio_init_all без параметров,
  3. когда завершит работу функция stdio_init_all, запускается функция while с параметром true,
  4. в функции while первой запускается по имени функция printf("Hello World!\n"),
  5. после неё запускается функция sleep_ms(1000),
  6. на этом тело функции while(true) закончилось, но так как while(true) - это бесконечный цикл, переходим к строке 4 и повторяем все до строки 6, и так бесконечно!

Как работают функции в нашей программе

Функция printf включена в стандартную библиотеку языка Си stdio, функции stdio_init_all и sleep_ms из библиотеки pico/stdlib, функция while входит в ядро языка Си.

Задание. Найдите в программе листинг 2 строку в которой подключается стандартная библиотека stdio.

Задание. В программе лист. 2 мы используем 5 функций, перечислите их имена.

В программе лист. 2. мы почти досконально разобрались как работает функция main, немного познакомились с функцией while, наверное вы поняли что функция printf отправляет через последовательный порт (serial) на монитор последовательного порта (serial monitor) текстовое сообщение "Hello World!\n" переданное в функцию printf в круглых скобках, как аргумент.

Разберёмся, что делают функции stdio_init_all и sleep_ms.

sleep_ms - создаёт задержку в выполнении программы, приостанавливает работу процессорного ядра микроконтроллера на количество миллисекунд указанное в параметре функции. sleep_ms(1000) создаёт задержку в 1 секунду.

stdio_init_all - настраивает USB-порт платы Raspberry Pi Pico как виртуальный последовательный порт. Дело в том, что USB и Serial (COM) порты передают информацию по 2-м проводам, но протоколы передачи у них разные и если предварительно не согласовать эти порты и протоколы, то функция prinf ничего не сможет передать в USB порт, а компьютер ничего не сможет прочитать из USB порта.

Комментарии

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

Символы // начинают однострочный комментарий. Компилятор игнорирует текст начиная с символов // и до конца строки. За символам // начинается текст (комментарий) для человека (программиста) и никак не используется компьютером.

С символов /* начинается многострочный комментарий, а символами */ он заканчивается. Текст многострочного комментария может располагаться на нескольких строках в программе на языке Си.

/*
 * Программа "Hello World" для Raspberry Pi Pico.
 * Вывод сообщения "Hello World!" через USB порт
 * в Serial Monitor по 1 строке каждую секунду.
 * Используются библиотеки stdio и stdlib.
 */

// Включение библиотек
#include <stdio.h>
#include "pico/stdlib.h"

int main()                           // Точка входа в программу
{
    stdio_init_all();                // Инициализация USB как Serial

    while (true) {
        printf("Hello, world!\n");
        sleep_ms(1000);              // Задержка 1 сек.
    }
}

Лист. 3. Программа "Hello World" с комментариями.

/*
 * Программа "Hello World" для Raspberry Pi Pico
 * Демонстрация базовой работы с USB-портом
 * 
 * Основные концепции:
 * - Инициализация периферии
 * - Работа с USB как последовательным портом
 * - Использование бесконечного цикла в embedded-системах
 */

// Стандартная библиотека ввода/вывода (нужна для printf)
#include <stdio.h>

// Библиотека специфичная для Pico (содержит функции инициализации и задержки)
#include "pico/stdlib.h"

// Главная функция - точка входа в программу
int main()
{
    // Инициализация USB для работы как последовательный порт
    // БЕЗ ЭТОЙ ФУНКЦИИ ПРОГРАММА НЕ СМОЖЕТ ВЫВОДИТЬ ДАННЫЕ НА КОМПЬЮТЕР!
    stdio_init_all();
    
    /*
     * Почему важна следующая задержка?
     * При подключении Pico к компьютеру требуется время (1-3 секунды),
     * чтобы операционная система распознала USB-устройство.
     * Без этой задержки первые сообщения могут быть потеряны!
     */
    sleep_ms(2500);  // 2500 миллисекунд = 2.5 секунды
    
    // Выводим информационное сообщение о начале работы
    // Используем \n\n чтобы "очистить" буфер терминала при подключении
    printf("\n\n--- Pico Hello World Program Started ---\n");

    /*
     * Бесконечный цикл - особенность embedded-программирования!
     * В отличие от программ на ПК, микроконтроллерные программы
     * никогда не должны завершать свою работу.
     */
    while (true) {
        // Выводим текст в последовательный порт (через USB)
        // \n - символ новой строки (LF - Line Feed)
        printf("Hello, world!\n");
        
        /*
         * Функция задержки - приостанавливает выполнение программы
         * Аргумент - время в миллисекундах (1000 мс = 1 секунда)
         * 
         * Важно: во время сна процессор может переходить
         * в режим пониженного энергопотребления
         */
        sleep_ms(1000);  // Пауза 1 секунда
        
        /*
         * Что происходит в цикле:
         * 1. Выводим сообщение
         * 2. Ждем 1 секунду
         * 3. Повторяем бесконечно
         * 
         * Без этого цикла программа завершилась бы сразу после первого вывода,
         * и USB-порт перестал бы работать!
         */
    }
    
    // Этот код никогда не выполнится из-за бесконечного цикла
    // Но формально возвращаем 0 (код успешного завершения)
    return 0;
}

Лист. 4. Программа "Hello World" доработанная и прокомментированная ИИ DeepSeek.