RP2040 (Raspberry Pi Pico)

Ответить
Аватара пользователя
ru0aog
Сообщения: 1078
Зарегистрирован: 30 сен 2021, 05:50
Позывной: ru0aog
Город: Красноярск
Имя: Станислав
Благодарил (а): 147 раз
Поблагодарили: 153 раза

RP2040 (Raspberry Pi Pico)

Сообщение ru0aog » 10 апр 2026, 10:39

Доброго дня всем!
Смотрю я на проект PicoRX и хочется мне применить подобные решения к своим задачам.

Попробую выполнить несколько "лабораторных" работ с китайской платой на RP2040

Благо стоит недорого.
Например
https://ali.click/5blf41m - 247 руб (3,2 $)
.
arduino.jpeg
.
Пробовать буду в среде Ардуино IDE с ядром Raspberry Pi Pico/RP2040/RP2350 от Earle F. Philhower, III
ядро (переносы убрать):
https://github.com/earlephilhower/
arduino-pico/releases/download/
global/package_rp2040_index.json

Тестовый скетч (мигает встроенным светодиодом)

Код: Выделить всё

void setup() {
  // Инициализируем встроенный светодиод как выход
  pinMode(LED_BUILTIN, OUTPUT);
}

void loop() {
  digitalWrite(LED_BUILTIN, HIGH); // Включаем (3.3V)
  delay(500);                      // Ждем полсекунды
  digitalWrite(LED_BUILTIN, LOW);  // Выключаем (0V)
  delay(500);                      // Ждем полсекунды
}

Аватара пользователя
ru0aog
Сообщения: 1078
Зарегистрирован: 30 сен 2021, 05:50
Позывной: ru0aog
Город: Красноярск
Имя: Станислав
Благодарил (а): 147 раз
Поблагодарили: 153 раза

RP2040 (Raspberry Pi Pico)

Сообщение ru0aog » 05 май 2026, 18:21

Черновая версия ядра маяка пока что лежит тут
https://github.com/ru0aog/RTTY-beacon-RP2040/tree/main
.
Ничего особенного: генератор, часы, термометр.
Для экономии энергии питание модулей в простое отключается, сишка ест побольше, поэтому ключ на транзисторе.
Можно ничего не компилировать, прошивка заключается в копировании файла с расширением UF2 на плату. Всё
BEACON_SCH_SI_05.JPG
.
SI_05.rar
(130.41 КБ) 2 скачивания


Аватара пользователя
ru0aog
Сообщения: 1078
Зарегистрирован: 30 сен 2021, 05:50
Позывной: ru0aog
Город: Красноярск
Имя: Станислав
Благодарил (а): 147 раз
Поблагодарили: 153 раза

RP2040 (Raspberry Pi Pico)

Сообщение ru0aog » 12 май 2026, 15:32

Реализовал передачу в моде IFKP 0.5

Среда Ардуино IDE 2.3.8
Плата RP2040 и модуль si5351
.
01.png
.
Смысл протокола такой:
- есть полоса 386 Гц,
в этой полосе с шагом 11.697 Гц расположены 33 частоты,
- когда канал занят (режим ожидания), передатчик последовательно перебирает эти частоты по кольцу, т.е. номер следующей частоты +1. Длительность каждой частоты - 0,5 сек,
- когда нужно передать какой-либо символ, следующая частота прыгает не +1, а на другую величину. Так кодируется 32 позиции: режим ожидания, 26 строчных букв (латинский алфавит), точка, пробел и три режима-модификатора, добавление которых даёт прописные буквы и спец.символы.
Какой-либо спец обработки инфы (перемножение/сжатие/интерливинг) не производится.

Код: Выделить всё

#include <Wire.h>

const uint8_t SI_POWER_PIN = 16;
const uint8_t SI_PIN_SDA   = 18;
const uint8_t SI_PIN_SCL   = 19;

#define SI5351_I2C_ADDR 0x60
uint64_t freq_10MHz = 10001067;
uint64_t Xtal_freq  = 25000000 * freq_10MHz / 10000000;

// 33 частоты IFKP
uint8_t DATA_IFKP[33][10];
uint8_t current_tone = 0;
// базовая частота, середина спектра после преобразования - на частоте 1500 Гц
// 1500-386/2 = 1307
uint32_t IFKP_Base_freq = 3601307;

// адреса и значения регистров si5351 при инициализации
const uint8_t INIT_REGS[] = {0x03, 0x10, 0x11, 0x12, 0xB7, 0x95, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, 0x20, 0x21, 0xB1, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E, 0x2F, 0x30, 0x31, 0x10, 0x03};
const uint8_t INIT_DATA[] = {0xFF, 0x80, 0x80, 0x80, 0xC0, 0x00, 0x00, 0x01, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0xA0, 0xA8, 0xE8, 0x00, 0x7B, 0xAB, 0xD4, 0x44, 0x08, 0x0F, 0x00};

// адреса регистров si5351 для смены частоты
const uint8_t FREQ_REGS[]  = {0x2A, 0x2B, 0x2C, 0x2D, 0x2E, 0x2F, 0x30, 0x31, 0x10, 0x03}; 

bool si5351_write_reg(uint8_t reg, uint8_t data) {
  for (int i = 0; i < 3; i++) {
    Wire1.beginTransmission(SI5351_I2C_ADDR);
    Wire1.write(reg);
    Wire1.write(data);
    if (Wire1.endTransmission() == 0) {
      return true; 
    }
    if (i < 2) {
      //Serial.print("SI5351: Retrying I2C... Attempt ");
      //Serial.println(i + 2);
      Wire1.begin();
      delay(10);
    }
  }
  return false;
}

void calculate_freq_bytes_mHz(uint64_t freq_mHz, uint8_t* out_data) {
// вычисление кодов частоты от 1 до 160 МГц (задаётся в миллигерцах)
    const uint32_t pll_multiplier = 36;
    uint64_t f_pll_mHz = (uint64_t)Xtal_freq * pll_multiplier * 1000ULL;
    uint32_t divider_int = (uint32_t)(f_pll_mHz / freq_mHz);
    uint64_t remainder_mHz = f_pll_mHz % freq_mHz;
    uint32_t ms_den = 1048575;
    uint32_t ms_num = (uint32_t)((remainder_mHz * ms_den) / freq_mHz);
    uint32_t p1 = 128 * divider_int + ((128 * ms_num) / ms_den) - 512;
    uint32_t p2 = 128 * ms_num - ms_den * ((128 * ms_num) / ms_den);
    uint32_t p3 = ms_den;
    out_data[0] = (p3 >> 8) & 0xFF;
    out_data[1] = p3 & 0xFF;
    out_data[2] = (p1 >> 16) & 0x03;
    out_data[3] = (p1 >> 8) & 0xFF;
    out_data[4] = p1 & 0xFF;
    out_data[5] = ((p3 >> 12) & 0xF0) | ((p2 >> 16) & 0x0F);
    out_data[6] = (p2 >> 8) & 0xFF;
    out_data[7] = p2 & 0xFF;
    out_data[8] = 0x0F;
    out_data[9] = 0x00;
}

void prepare_ifkp_frequencies(uint32_t base_hz) {
// заполняет таблицу базовых частот DATA_IFKP[33][10]
    uint64_t base_mHz = (uint64_t)base_hz * 1000ULL;
    for (int i = 0; i < 33; i++) {
        // Шаг 386/33 = 11.697 Гц
        uint64_t freq_mHz = base_mHz + ((uint64_t)i * 11697ULL);
        calculate_freq_bytes_mHz(freq_mHz, DATA_IFKP[i]);
    }
    //Serial.println("DATA_IFKP: 33 Tones prepared");
}

void set_ifkp_tone(uint8_t tone_index) {
    if (tone_index > 32) return;
    for (uint8_t i = 0; i < 8; i++) {
        si5351_write_reg(FREQ_REGS[i], DATA_IFKP[tone_index][i]);
    }
}

void send_delta(uint8_t delta) {
    current_tone = (current_tone + delta) % 33;
    set_ifkp_tone(current_tone);
    delay(500); 
}

void send_ifkp_char(char c) {
    // Вывод символа в консоль для контроля
    //Serial.print(c);

    // --- Одиночные буквы (офсет +1) ---
    if (c >= 'a' && c <= 'z') send_delta((c - 'a' + 1) + 1); 
    else if (c == '.')  { send_delta(27 + 1);}
    else if (c == ' ')  { send_delta(28 + 1);}

    // --- Заглавные буквы (Модификатор 29 + офсет 1) ---
    else if (c >= 'A' && c <= 'Z') {
        send_delta((c - 'A' + 1) + 1); 
        send_delta(29 + 1); 
    }

    // --- Цифры (Модификатор 30) ---
    else if (c >= '1' && c <= '9') {
        send_delta((c - '1' + 1) + 1); 
        send_delta(30 + 1); 
    }
    else if (c == '0') {
        send_delta(10 + 1); 
        send_delta(30 + 1); 
    }
    else if (c == '\n')  { send_delta(28 + 1); send_delta(30 + 1);}

    // --- Спецсимволы (Модификатор 29)
    else if (c == '@')  { send_delta(0 + 1);   send_delta(29 + 1); }
    else if (c == ',')  { send_delta(27 + 1);  send_delta(29 + 1); }
    else if (c == '?')  { send_delta(28 + 1);  send_delta(29 + 1); }

    // --- Спецсимволы (Модификатор 30)
    else if (c == '~')  { send_delta(0 + 1);  send_delta(30 + 1); }
    else if (c == '!')  { send_delta(11 + 1); send_delta(30 + 1); }
    else if (c == '"')  { send_delta(12 + 1); send_delta(30 + 1); }
    else if (c == '#')  { send_delta(13 + 1); send_delta(30 + 1); }
    else if (c == '$')  { send_delta(14 + 1); send_delta(30 + 1); }
    else if (c == '%')  { send_delta(15 + 1); send_delta(30 + 1); }
    else if (c == '&')  { send_delta(16 + 1); send_delta(30 + 1); }
    else if (c == '\'') { send_delta(17 + 1); send_delta(30 + 1); }
    else if (c == '(')  { send_delta(18 + 1); send_delta(30 + 1); }
    else if (c == ')')  { send_delta(19 + 1); send_delta(30 + 1); }
    else if (c == '*')  { send_delta(20 + 1); send_delta(30 + 1); }
    else if (c == '+')  { send_delta(21 + 1); send_delta(30 + 1); }
    else if (c == '-')  { send_delta(22 + 1); send_delta(30 + 1); }
    else if (c == '/')  { send_delta(23 + 1); send_delta(30 + 1); }
    else if (c == ':')  { send_delta(24 + 1); send_delta(30 + 1); }
    else if (c == ';')  { send_delta(25 + 1); send_delta(30 + 1); }
    else if (c == '<')  { send_delta(26 + 1); send_delta(30 + 1); }
    else if (c == '>')  { send_delta(27 + 1); send_delta(30 + 1); }

    // --- Спецсимволы (Модификатор 31)
    else if (c == '=')  { send_delta(0 + 1);  send_delta(31 + 1); }
    else if (c == '[')  { send_delta(1 + 1);  send_delta(31 + 1); }
    else if (c == '\\')  { send_delta(2 + 1);  send_delta(31 + 1); }
    else if (c == ']')  { send_delta(3 + 1);  send_delta(31 + 1); send_delta(4 + 1);  send_delta(31 + 1);}
    else if (c == '_')  { send_delta(5 + 1);  send_delta(31 + 1); }
    else if (c == '{')  { send_delta(6 + 1);  send_delta(31 + 1); }
    else if (c == '|')  { send_delta(7 + 1);  send_delta(31 + 1); }
    else if (c == '}')  { send_delta(8 + 1);  send_delta(31 + 1); }
    else if (c == '`')  { send_delta(9 + 1);  send_delta(31 + 1); }
    else if (c == '\xB1')  { send_delta(10 + 1);  send_delta(31 + 1); }
    else if (c == '\xF7')  { send_delta(11 + 1);  send_delta(31 + 1); }
    else if (c == '\xB0')  { send_delta(12 + 1);  send_delta(31 + 1); }
    else if (c == '\xD7')  { send_delta(13 + 1);  send_delta(31 + 1); }
    else if (c == '\xA3')  { send_delta(14 + 1);  send_delta(31 + 1); }
    else if (c == '\x7F')  { send_delta(28 + 1);  send_delta(31 + 1); }
}

void send_string(const char* str) {
    for (int i = 0; str[i] != '\0'; i++) {
        send_ifkp_char(str[i]);
    }
}

void SI_POWER_ON() {
    // включаем питание модуля si5351
    digitalWrite(SI_POWER_PIN, LOW);
    // подготавливаем таблицу из 33 тонов для IFKP
    prepare_ifkp_frequencies(IFKP_Base_freq);
    // ждём заряда конденсаторов питания
    // и запускаем шину I2C
    delay(100);
    Wire1.begin();
    // проверяем наличие модуля si5351
    if (si5351_write_reg(INIT_REGS[0], INIT_DATA[0])) {
        //Serial.println("VFO SI5351: OK");
    } else {
        //Serial.println("VFO SI5351: NOT FOUND!");
        return;
    }
    // вписываем последовательность инициализации si5351
    for (int i = 1; i < 25; i++) {
        if (!si5351_write_reg(INIT_REGS[i], INIT_DATA[i])) {
        //Serial.print("VFO SI5351: Failed at reg ");
        //Serial.println(INIT_REGS[i], HEX);
        }
    }
}

void SI_POWER_OFF() {
    // выключить питание si5351
    digitalWrite(SI_POWER_PIN, HIGH);
}

void setup() {
    pinMode(SI_POWER_PIN, OUTPUT);
    Wire1.setSDA(SI_PIN_SDA);
    Wire1.setSCL(SI_PIN_SCL);
    Wire1.begin();
    Wire1.setClock(400000);
    SI_POWER_ON();
}

void loop() {
    // лесенка синхронизации
    for(int i = 0; i < 10; i++) send_delta(1);
    // передача текста
    send_string("\n\n");
    send_string("transmitting mode IFKP 0.5\n");
    send_string("0123456789(!#$%&*+-.,/:;<>?) The quick brown fox jumps over the lazy dog (english)\n");
    send_string(" .,:;@!?~#$%()[|]{}<>+-*/_ABCDEFGHIJKLMNOPQRSTUVWXYZ\n");
    send_string("transmitting is over\n");
    send_string("\n\n");
}
P.S.
На картинке программа FLDIGI принимает тоны моего маяка.
Маяк на плате RP2040 управляет модулем si5351, который формирует частотные посылки на 3600 кГц. Посылки принимаются трансивером tx-500 и воспроизводятся динамиком тангенты. На тангенте лежит микрофон, подключенный к звуковой карте компа, с которой программа FLDIGI принимает тоны и показывает принятый текст :)
IFKP_01.rar
(469.35 КБ) 0 скачиваний

Ответить

Вернуться в «Микроконтроллеры и программирование»