Самый лучший способ изучить новый язык программирования - Это сразу начать писать на нём программы.
Брайн Керниган и Деннис Ритчи
Традиционно, первой программой, с которой начинают изучение нового языка бывает программа "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 создаёт бесконечный цикл в котором бесконечно будет выполняться тело цикла.
Подытожим:
- при старте программы листинг 2 автоматически запускается функция main без параметров,
- в ней, по имени вызывается функция stdio_init_all без параметров,
- когда завершит работу функция stdio_init_all, запускается функция while с параметром true,
- в функции while первой запускается по имени функция printf("Hello World!\n"),
- после неё запускается функция sleep_ms(1000),
- на этом тело функции 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.