Общие сведения
В этой статье мы объясним как установить поддержку плат Raspberry Pi Pico в Arduino IDE.
Для установки поддержки этих плат достаточно выполнить несколько простых шагов. Перед их выполнением убедитесь, что на ПК на котором они выполняются есть подключение к сети Интернет и доступ к нему для Arduino IDE не заблокирован в Брендмауре Windows.
1 - Откройте менеджер плат Arduino IDE
Выберете пункт меню "Инструметы"
->"Плата:"
->"Менеджер плат..."
2 - Найдите плату в списке
Введите rp2040 в поле поиска
3 - Установите поддержку плат
Найдите строку Arduino Mbed OS RP2040 Boards и нажмите "Установить"
Начнётся установка. Если появится диалоговое окно "Контроль учётных записей", нажмите "Да".
Если появятся окна "Безопасность Windows" с установкой USB устройств, нажмите "Установить"
4 - проверка
Если Вы ещё не подключили плату к ПК, подключите её зажав кнопку "BOOTSEL" на плате.
Выберете пункт меню "Инструменты"
->"Плата:"
->"Arduino Mbed OS RP2040 Boards"
->"Rasbperry Pi Pico"
Начнётся настройка устройства
После завершения настройки в меню "Инструменты"
->"Порт"
должен появится новый COM порт. Выберете его.
Загрузите проверочный скетч в плату (например Blink)
Готово!
5 - устранение неполадок (Нет COM порта в списке)
Способ 1
Если Вы уже использовали плату с MicroPython и после этого сбрасывали на заводские настройки при помощи flash_nuke.uf2
файла, то после подключения к ПК в Arduino IDE порт платы может быть не виден, но при этом внизу окна Arduino IDE будет показан COM порт отсутствующий в списке.
Просто нажмите "Загрузить скетч" не выбирая никаких портов и после загрузки скетча и перезагрузки платы появится новый порт в списке.
Способ 2
Если внизу окна Arduino IDE нет порта не из списка портов или в меню порт уже выбран какой-либо порт, то:
- Зажмите кнопку "BOOTSEL" на плате при подключении к ПК. Плата определится как флеш карта.
- Скопируйте на неё вот этот файл. Дождитесь перезагрузки микроконтроллера.
- В меню
"Инструменты"
->"Порт"
появится новый порт. Выберете его и загрузите в плату любой скетч, выбрав при этом Raspberry Pi Pico в меню плат. - После загрузки скетча плата определится как Raspberry Pi Pico.
Сброс на заводские настройки
Не важно, что вы загружали в плату, её всегда можно перезагрузить в режиме флеш-карты и сбросить на заводские настройки. Для этого:
- Зажмите кнопку "BOOTSEL" на плате при подключении к ПК. Плата определится как флеш карта.
- Скопируйте на неё
flash_nuke.uf2
файл с сайта производителя. Дождитесь перезагрузки микроконтроллера. - Микроконтроллер сброшен на заводские настройки.
Назначение выводов Raspberry Pi Pico.
Перед тем как подключить к плате внешнее питание или устройства, необходимо разобраться с назначением выводов:
Распиновка платы:
Выводы питания на плате:
- Вывод питания +VBUS (вход/выход) подключен к линии +5В от разъёма USB.
- Вывод питания +VSYS (вход/выход) получает напряжение +5В от шины +VBUS через диод на плате. Если не используется питание от USB, то на вывод +VSYS можно подать внешнее питание от +1,8 до +5,5В. Напряжение шины +VSYS поступает на вход DC-DC преобразователя на плате, который может как повышать, так и понижать входное напряжение, до выходного +3,3В.
- Вход разрешения работы 3V3_EN этот вход разрешает работу DC-DC преобразователя. Линия 3V3_EN подтянута до напряжения +VSYS, то есть по умолчанию работа DC-DC разрешена. Если вход 3V3_EN соединить с GND, то DC-DC преобразователь отключится и пропадёт напряжение 3,3В питающее микроконтроллер.
- Выход питания +3V3(OUT) получает напряжение +3,3В с выхода DC-DC преобразователя RT6150-33GQW расположенного на плате. Этот выход можно использовать для питания внешних устройств. Максимальный ток 800 мА (включая ток потребляемый всеми чипами платы).
- Вход +ADC_VREF позволяет задать опорное напряжение аналого-цифрового преобразователя (АЦП) ниже +3,3В. Максимальное значение АЦП будет соответствовать напряжению на данном выводе. Линия +ADC_VREF подключена на плате к линии +3V3(OUT) через сопротивление 200 Ом, значит опорное напряжение по умолчанию равно +3,3В.
Выводы подключённые к узлам схемы платы:
Из распиновки видно, что на плате не выведены выводы: GP23, GP24, GP25, GP29, но они имеются у микроконтроллера и подключены к отдельным узлам схемы на плате.
- Вывод GP29 - подключён к шине питания +VSYS через делитель 1/3. Данный вывод можно использовать как аналоговый вход для вычисления напряжения на шине +VSYS.
- Вывод GP25 - подключён к светодиоду на плате, через токоограничительный резистор 470 Ом. Данный вывод можно использовать как выход для включения (1) и выключения (0) светодиода.
- Вывод GP24 - подключен к шине питания +VBUS через делитель 9/14. Данный вывод можно использовать как цифровой вход для определения наличия напряжения на шине +VBUS.
- Вывод GP23 - подключён к входу PS преобразователя RT6150-33GQW на плате. Вывод может управлять режимом работы DC-DC преобразователя. Если на вывод подать 0, то преобразователь будет работать в режиме подбора частоты под малую нагрузку (по умолчанию), если на вывод подать 1, то преобразователь будет работать в режиме фиксированной частоты. При высокой нагрузке DC-DC преобразователь самостоятельно переходит в режим фиксированной частоты, независимо от состояния на входе PS.
- Кнопка BOOTSEL - подключает линию GND к выводу CS чипа flash-памяти W25Q16JVUXIQ расположенного на плате.
Примеры работы с Raspberry Pi Pico в Arduino IDE.
Программы (скетчи) для работы с Raspberry Pi Pico в Arduino IDE не сильно отличаются от скетчей написанных в Arduino IDE для работы с платами Arduino, но есть некоторые отличия.
Управление выводами Raspberry Pi Pico в Arduino IDE:
При работе с выводами используются стандартные функции: pinMode()
, digitalRead()
, digitalWrite()
, analogRead()
, analogWrite()
, которым указываются номера GPIO от 0 до 29, а не номера выводов платы по порядку от 1 до 40.
- Конфигурация выводов:
Синтаксис:pinMode( номер вывода, режим работы );
Возвращаемое значение: Нет.
pinMode(0, INPUT); // Конфигурируем вывод GP0 как вход. pinMode(1, OUTPUT); // Конфигурируем вывод GP1 как выход. pinMode(2, INPUT_PULLUP); // Конфигурируем вывод GP2 как вход с подтяжкой к 3V3. pinMode(3, INPUT_PULLDOWN); // Конфигурируем вывод GP3 как вход с прижатием к GND.
Примечание: Функция выполняется несколько миллисекунд.
- Чтение логического уровня:
Синтаксис:digitalRead( номер вывода );
Возвращаемое значение: 0 (LOW) или 1 (HIGH).
bool i = digitalRead(0); // Читаем логический уровень с вывода GP0. bool j = digitalRead(1); // Читаем логический уровень с вывода GP1.
Примечание: Перед чтением нужно сконфигурировать вывод.
- Чтение аналогово сигнала:
Синтаксис:analogRead( номер вывода );
Возвращаемое значение: int 0...1023 (по умолчанию). Диапазон можно изменить.
int i = analogRead(26); // Читаем аналоговый уровень с вывода GP26 (A0). int j = analogRead(A0); // Читаем аналоговый уровень с вывода A0 (GP26).
Примечание: Выводы можно не конфигурировать.
- Изменение диапазона при чтении аналогово сигнала:
Синтаксис:analogReadResolution( разрядность в битах );
Возвращаемое значение: Нет.
analogReadResolution( 12 ); // Указываем разрядность АЦП = 12 бит. int j = analogRead(A0); // Теперь функция вернёт значение от 0 до 4095.
Примечание: Функцию достаточно вызвать однократно, например, в коде setup(). Функция принимает значения от 1 до 12. Можно указать разрядность выше поддерживаемой, вплоть до 32 бит, но тогда значения возвращаемые функциями analogRead() будут просто усреднены. Если функцию не вызывать, то разрядность АЦП по умолчанию будет 10 бит, а диапазон значений возвращаемых функциями analogRead() будет лежать в пределах 0 ... 1023.
- Указание логического уровня:
Синтаксис:digitalWrite( номер вывода, уровень );
Возвращаемое значение: Нет.
digitalWrite(2, LOW ); // Устанавливаем на выводе GP2 низкий уровень 0. digitalWrite(3, HIGH); // Устанавливаем на выводе GP3 высокий уровень 1.
Примечание: Перед указанием уровня, вывод нужно сконфигурировать как выход.
- Указание ШИМ:
Синтаксис:analogWrite( номер вывода, коэффициент ШИМ );
Возвращаемое значение: Нет.
analogWrite(2, 127); // Устанавливаем на ШИМ 127 (по умолчанию 50%). analogWrite(3, 255); // Устанавливаем на ШИМ 255 (по умолчанию 100%).
Примечание: Перед указанием ШИМ, вывод нужно сконфигурировать как выход. Все выводы поддерживают ШИМ, но одновременно можно использовать ШИМ не более чем на 16 выводах.
- Изменение диапазона при указании ШИМ:
Синтаксис:analogWriteResolution( разрядность в битах );
Возвращаемое значение: Нет.
analogWriteResolution( 16 ); // Указываем разрядность ШИМ = 16 бит. analogWrite(4, 255); // Устанавливаем на выводе GP4 значение ШИМ 255 (0.4%) analogWrite(5, 65535); // Устанавливаем на выводе GP5 значение ШИМ 65535 (100%)
Примечание: Функцию достаточно вызвать однократно, например, в коде setup(). Функция принимает значения от 8 до 16. Если указать разрядность выше или ниже аппаратных, то коэффициент указанный функции analogWrite(), будет либо усечен, либо дополнен нулями. Если функцию не вызывать, то разрядность ШИМ по умолчанию будет 8 бит, а диапазон значений принимаемых функциями analogWrite() будет лежать в пределах 0 ... 255.
Работа с шинами Raspberry Pi Pico: UART, I2C, SPI в Arduino IDE:
На плате Raspberry Pi Pico имеются две шины UART, две шины I2C и две шины SPI.
Главное отличие шин на плате Raspberry Pi Pico от шин на платах Arduino Uno, Mini, Nano, Mega и т.д. заключается в том, что вы можете выбирать выводы шин из представленных на распиновке (см. раздел Назначение выводов).
- Шина UART:
Библиотека:Serial.h
(предустановлена в Arduino IDE).
Класс:UART
Объекты: Serial
- для работы с шиной UART-USB (монитор последовательного порта).Serial1
- для работы с шиной UART-0 (выводы по умолчанию GP0=TX, GP1=RX).- Для работы с шиной UART-0 можно создать свой объект указав другие выводы.
- Для работы с шиной UART-1 нужно создать свой объект указав выводы.
Пример использования объектов шины UART-USB и UART-0 по умолчанию:
void setup(){ // Serial.begin(9600); // Инициируем работу с шиной UART-USB на скорости 9600. Serial1.begin(9600); // Инициируем работу с шиной UART-0 на скорости 9600. } // // void loop(){ // Serial.println("Hello"); // Отправляем текст "Hello" по шине UART-USB (в монитор последовательного порта). Serial1.println("world"); // Отправляем текст "world" по шине UART-0. // Задержка перед новой отправкой: // delay(1000); // Ждём 1 секунду. } //
Пример создания своего объекта с указанием выводов TX и RX:
UART MySerial(4, 5); // Создаём экземпляр класса UART библиотеки Serial. // Указав выводы TX=4, RX=5. void setup(){ // MySerial.begin(9600); // Инициируем работу с шиной UART на скорости 9600. } // // void loop(){ // MySerial.println("Hello"); // Отправляем текст "Hello" по шине UART. // Задержка перед новой отправкой: // delay(1000); // Ждём 1 секунду. } //
В примере указаны выводы 4, 5, значит используется шина UART-1.
Если указать выводы 0,1; 12,13; 16,17; то будет использована шина UART-0.
Если указать выводы 4,5; 8,9; то будет использована шина UART-1.
Можно одновременно использовать шины: UART-USB, UART-0 и UART-1.
Пример использования объекта шины UART для работы с библиотеками iArduino:
#include <iarduino_GSM.h> // Подключаем библиотеку для работы с модулем GSM. // UART MySerial(0, 1); // Создаём экземпляр класса UART библиотеки Serial, указав выводы TX=0, RX=1. iarduino_GSM gsm(7); // Создаём объект gsm для работы с модулем GSM, указав вывод PWR. // void setup(){ // gsm.begin(MySerial); // Инициируем работу с модулем GSM, указывая объект шины UART. } // // void loop(){ // // Работа с модулем GSM... // } //
Объектам библиотек iArduino можно указывать как шину UART-0, так и шину UART-1.
Рекомендуем ознакомиться с кратким руководством по работе с шиной UART.
- Шина I2C:
Библиотека:Wire.h
(предустановлена в Arduino IDE).
Класс:MbedI2C
Объекты: Wire
- для работы с шиной I2C-0 (выводы по умолчанию GP4=SDA, GP5=SCL).- Для работы с шиной I2C-0 можно создать свой объект указав другие выводы.
- Для работы с шиной I2C-1 нужно создать свой объект указав выводы.
Пример использования объекта шины I2C по умолчанию (Wire
):
#include <Wire.h> // Подключаем библиотеку для работы с шиной I2C. // void setup(){ // Wire.begin(); // Инициируем работу с шиной I2C в качестве мастера. } // // void loop(){ // Wire.beginTransmission(0x09); // Инициируем передачу данных к устройству 0x09. Wire.write(0x01); // Помещаем в буфер адрес регистра. Wire.write(0x02); // Помещаем в буфер байт для записи в регистр. // Wire.write(0x03); // Следующие байт в следующий регистр. Wire.endTransmission(); // Выполняем инициированную ранее передачу данных. // Задержка перед новой записью: // delay(1000); // Ждём 1 секунду. } //
Пример создания своего объекта с указанием выводов SDA и SCL:
#include <Wire.h> // Подключаем библиотеку для работы с шиной I2C. MbedI2C MyWire(2, 3); // Создаём экземпляр класса MbedI2C библиотеки Wire. // Указав выводы SDA=2, SCL=3. void setup(){ // MyWire.begin(); // Инициируем работу с шиной I2C в качестве мастера. } // // void loop(){ // MyWire.beginTransmission(0x09); // Инициируем передачу данных к устройству 0x09. MyWire.write(0x01); // Помещаем в буфер адрес регистра. MyWire.write(0x02); // Помещаем в буфер байт для записи в регистр. // MyWire.write(0x03); // Следующие байт в следующий регистр. MyWire.endTransmission(); // Выполняем инициированную ранее передачу данных. // Задержка перед новой записью: // delay(1000); // Ждём 1 секунду. } //
В примере указаны выводы 2, 3, значит используется шина I2C-1.
Если указать выводы 0,1; 4,5; 8,9; 12,13; 16,17; 20,21; то будет использована шина I2C-0.
Если указать выводы 2,3; 6,7; 10,11; 14,15; 18,19; 26,27; то будет использована шина I2C-1.
Можно одновременно использовать шину I2C-0 и I2C-1.
Пример использования объекта шины I2C для работы с библиотеками iArduino:
#include <Wire.h> // Подключаем библиотеку для работы с шиной I2C. #include <iarduino_I2C_Bumper.h> // Подключаем библиотеку для работы с бампером. // MbedI2C MyWire(0, 1); // Создаём экземпляр класса MbedI2C библиотеки Wire, указав выводы SDA=0, SCL=1. iarduino_I2C_Bumper bum(0x09); // Создаём объект bum для работы с бампером, указав его адрес на шине. // void setup(){ // bum.begin(&MyWire); // Инициируем работу с бампером, указав объект шины I2C. } // // void loop(){ // // Работа с бампером ... // } //
Объектам библиотек iArduino можно указывать как шину I2C-0, так и шину I2C-1.
Рекомендуем ознакомиться с кратким руководством по работе с шиной I2C.
- Шина SPI:
Библиотека:SPI.h
(предустановлена в Arduino IDE).
Класс:MbedSPI
Объекты: SPI
- для работы с шиной SPI-0
(выводы по умолчанию GP16=MOSI, GP17=SS, GP18=SCK, GP19=MISO).- Для работы с шиной SPI-0 можно создать свой объект указав другие выводы.
- Для работы с шиной SPI-1 нужно создать свой объект указав выводы.
Пример использования объекта шины SPI по умолчанию (SPI
):
#include <SPI.h> // Подключаем библиотеку для работы с шиной SPI. uint8_t pinSS = 17; // Определяем номер вывода SS. // // Определяем настройки SPI: // SPISettings MySet(10000000, MSBFIRST, SPI_MODE0); // 10МГц, старшим битом вперёд, режим 0. // void setup(){ // SPI.begin(); // Инициируем работу с шиной SPI. pinMode(pinSS, OUTPUT); // Конфигурируем вывод SS как выход. digitalWrite(pinSS, HIGH); // Устанавливаем высокий уровень на выводе SS. } // // void loop(void){ // uint8_t dataIn = 0; // Переменная для получения байта. uint8_t dataOut = 0; // Переменная для передачи байта. SPI.beginTransaction(MySet); // Применяем настройки (не обязательно, если используются настройки по умолчанию). digitalWrite(pinSS, LOW); // Устанавливаем низкий уровень на выводе SS. dataIn = SPI.transfer(dataOut); // Передаём dataOut, получаем dataIn. dataIn = SPI.transfer(dataOut); // Можно передать/получить требуемое количество байт. digitalWrite(pinSS, HIGH); // Устанавливаем высокий уровень на выводе SS. SPI.endTransaction(); // Отменяем настройки (если они применялись функцией beginTransaction). // Задержка перед новыми пакетами: // delay(10); // Ждём 10 мс. } //
Пример создания своего объекта с указанием выводов MISO, MOSI, SCK и SS:
#include <SPI.h> // Подключаем библиотеку для работы с шиной SPI. MbedSPI MySPI(8, 11, 10); // Создаём экземпляр класса MbedSPI библиотеки SPI, указав выводы MISO=12, MOSI=11, SCK=10. uint8_t pinSS = 9; // Определяем номер вывода SS. // // Определяем настройки SPI: // SPISettings MySet(8000000, LSBFIRST, SPI_MODE3); // 8МГц, младшим битом вперёд, режим 3. // void setup(){ // MySPI.begin(); // Инициируем работу с шиной SPI. pinMode(pinSS, OUTPUT); // Конфигурируем вывод SS как выход. digitalWrite(pinSS, HIGH); // Устанавливаем высокий уровень на выводе SS. } // // void loop(void){ // uint8_t dataIn = 0; // Переменная для получения байта. uint8_t dataOut = 0; // Переменная для передачи байта. MySPI.beginTransaction(MySet); // Применяем настройки (не обязательно, если используются настройки по умолчанию). digitalWrite(pinSS, LOW); // Устанавливаем низкий уровень на выводе SS. dataIn = MySPI.transfer(dataOut); // Передаём dataOut, получаем dataIn. dataIn = MySPI.transfer(dataOut); // Можно передать/получить требуемое количество байт. digitalWrite(pinSS, HIGH); // Устанавливаем высокий уровень на выводе SS. MySPI.endTransaction(); // Отменяем настройки (если они применялись функцией beginTransaction). // Задержка перед новыми пакетами: // delay(10); // Ждём 10 мс. } //
В примере указаны выводы 8, 11, 10, 9 значит используется шина SPI-1.
Если указать выводы 0,3,2,1; 4,7,6,5; 16,19,18,17; то будет использована шина SPI-0.
Если указать выводы 8,11,10,9; 12,15,14,13; то будет использована шина SPI-1.
Можно одновременно использовать шину SPI-0 и SPI-1.
При создании объекта шины SPI указаны выводы MISO, MOSI, SCK, а вывод SS в режиме мастера управляется отдельно. В режиме ведомого используется вывод SS по умолчанию для выбранной шины SPI.
Рекомендуем ознакомиться с кратким руководством по работе с шиной SPI.
Многозадачность:
Многозадачность реализуется предустановленной в Arduino IDE библиотекой Scheduler. Библиотека автоматически устанавливается при добавлении платы.
#include <Scheduler.h> // Подключаем библиотеку многозадачности. // void setup(){ // Serial.begin(9600); // Scheduler.startLoop(MyLoop1); // Добавляем функцию которая будет выполняться параллельно циклу loop(). Scheduler.startLoop(MyLoop2); // Добавляем функцию которая будет выполняться параллельно циклу loop(). } // // void loop(){ // Serial.println(1); // delay(1500); // } // // void MyLoop1(){ // while( millis()<500 ); // Задержка в течении первых 500 мс после старта. Serial.println(2); // delay(1500); // } // // void MyLoop2(){ // while( millis()<1000 ); // Задержка в течении первых 1000 мс после старта. Serial.println(3); // delay(1500); // } //
В примере параллельно выполняются коды функций: loop(), MyLoop1() и MyLoop2(). Можно добавить и больше функций.
Функция loop() отправляет 1 в монитор последовательного порта, с интервалом 1500мс.
В начале кода функции MyLoop1() имеется цикл while() который задержит начало выполнения этой функции на 500 мс. Далее функция будет отправлять 2, с интервалом 1500 мс.
В начале кода функции MyLoop2() имеется цикл while() который задержит начало выполнения этой функции на 1000 мс. Далее функция будет отправлять 3, с интервалом 1500 мс.
В результате в мониторе последовательного порта, через каждые 500 мс будут поочерёдно появляться цифры: 1, 2, 3.
Обсуждение