Karat3.ru

Клуб каратистов
Текущее время: 14 май 2026, 01:09

Часовой пояс: UTC




Начать новую тему  Ответить на тему  [ 44 сообщения ]  На страницу Пред. 1 2 3 4 5 След.
Автор Сообщение
 Заголовок сообщения: Re: RP2040
СообщениеДобавлено: 10 апр 2026, 16:18 
Не в сети

Зарегистрирован: 25 ноя 2024, 13:52
Сообщения: 97
Лаб.работа №2
Сделаем такой конечный автомат, который бы без участия центральных процессоров генерировал частоты на выходном пине в зависимости от состояния пина входного.
Когда на входе 1 - 1170 Гц, когда 0 - 1000 Гц.

====
Подробно распишу позже.
====
Код:
#include "hardware/pio.h"
#include "hardware/clocks.h"

const float PIO_FREQ_FSK = 117000.0f;
const uint  pin_fsk_in   = 14;
const uint  pin_fsk_out  = 15;

PIO pio = pio0;
uint sm_fsk; 

static const uint16_t fsk_modulator_instructions[] = {
    0x00C7, // 0: jmp pin 7  (Если пин=1, прыгаем на метку 7)
    0xfc01, // 1: set pins, 1 [28]
    0xbc42, // 2: nop [28]
    0xfc00, // 3: set pins, 0 [28]
    0xbb42, // 4: nop [27]
    0x00C7, // 5: jmp pin 7
    0x0001, // 6: jmp 1
    0xf801, // 7: set pins, 1 [24]
    0xb842, // 8: nop [24]
    0xf800, // 9: set pins, 0 [24]
    0xb742  // 10: nop [23]
};

static const struct pio_program fsk_mod_prog = {
    .instructions = fsk_modulator_instructions,
    .length = 11,
    .origin = -1,
};

void setup() {
    uint offset = pio_add_program(pio, &fsk_mod_prog);
    sm_fsk = pio_claim_unused_sm(pio, true);

    pinMode(pin_fsk_in, INPUT_PULLDOWN);
    pio_gpio_init(pio, pin_fsk_out);
    pio_gpio_init(pio, pin_fsk_in);
    pio_sm_set_consecutive_pindirs(pio, sm_fsk, pin_fsk_out, 1, true);
    pio_sm_set_consecutive_pindirs(pio, sm_fsk, pin_fsk_in, 1, false); // Вход

    pio_sm_config c = pio_get_default_sm_config();
    
    // Настройки пинов
    sm_config_set_set_pins(&c, pin_fsk_out, 1);
    sm_config_set_jmp_pin(&c, pin_fsk_in); // Пин 14 управляет выбором частоты
    
    // Делитель для получения частоты PIO 117 кГц
    float div = (float)clock_get_hz(clk_sys) / PIO_FREQ_FSK;
    sm_config_set_clkdiv(&c, div);
    sm_config_set_wrap(&c, offset, offset + 10);
    pio_sm_init(pio, sm_fsk, offset, &c);
    pio_sm_set_enabled(pio, sm_fsk, true);
}

void loop() {
    // Ядро полностью свободно! Модуляция идет на автомате в PIO.
}
Так как мы приняли для точного совпадения частот частоту PIO = 117000 Гц,
то на частоте 1000 Гц получается 117 тактов, которые не делятся поровну.
В результате при точном совпадении частоты немного страдает скважность - 49,6% вместо 50,0.
Вложение:
01.jpg
01.jpg [ 137.42 КБ | 102 просмотра ]
.
Зато на частоте 1170 Гц всё идеально
Вложение:
02.jpg
02.jpg [ 137.52 КБ | 102 просмотра ]
.
Вложение:
1000_1170_SHIFT.rar [215.65 КБ]
2 скачивания


Вернуться к началу
 Заголовок сообщения: Re: RP2040
СообщениеДобавлено: 11 апр 2026, 06:46 
Не в сети

Зарегистрирован: 25 ноя 2024, 13:52
Сообщения: 97
Лаб.работа №3

Сделаем такой конечный автомат, который бы передавал входящий байт на нужной скорости.
Код:
#include "hardware/pio.h"
#include "hardware/clocks.h"

const float RTTY_BAUD = 45.45f;
const float PIO_FREQ = RTTY_BAUD * 2071.0f; 
const uint pin_data_out = 15; 

uint sm_id;

static const uint16_t rtty_modem_instructions[] = {
    // Адреса внутри программы (0-6)
    0x80a0, // 0: pull block
    0xe047, // 1: set y, 7
    0x7401, // 2: out pins, 1 [20]
    0xe03f, // 3: set x, 31        (Инициализируем счетчик задержки)
    0xbf42, // 4: nop [31]         (Пауза внутри цикла X)
    0x0074, // 5: jmp x-- 4 [31]   (Вложенный цикл: 32 итерации по 64 такта)
    0x0082, // 6: jmp y-- 2
    0xe001, // 7: set pins, 1    
};

static const struct pio_program rtty_prog = {
    .instructions = rtty_modem_instructions,
    .length = 8,
    .origin = -1,
};

void setup() {
    set_sys_clock_khz(125000, true);

    uint offset = pio_add_program(pio0, &rtty_prog);
    sm_id = pio_claim_unused_sm(pio0, true);

    // ДИНАМИЧЕСКАЯ КОРРЕКЦИЯ ПРЫЖКОВ
    // Инструкция 5 (jmp x-- 4): прыжок на offset + 4 с задержкой 31
    pio0->instr_mem[offset + 5] = 0x0040 | (offset + 4) | (31 << 8);
    // Инструкция 6 (jmp y-- 2): прыжок на offset + 2
    pio0->instr_mem[offset + 6] = 0x0080 | (offset + 2);

    pio_gpio_init(pio0, pin_data_out);
    pio_sm_set_consecutive_pindirs(pio0, sm_id, pin_data_out, 1, true);

    pio_sm_config c = pio_get_default_sm_config();
    sm_config_set_out_pins(&c, pin_data_out, 1);
    sm_config_set_set_pins(&c, pin_data_out, 1);
    sm_config_set_out_shift(&c, false, true, 8); 
    
    // НАСТРОЙКА ЗАЦИКЛИВАНИЯ (чтобы не зависало после 7-й инструкции)
    sm_config_set_wrap(&c, offset + 0, offset + 7);
    
    float div = (float)clock_get_hz(clk_sys) / PIO_FREQ;
    sm_config_set_clkdiv(&c, div);
    
    pio_sm_init(pio0, sm_id, offset, &c);
    pio_sm_set_enabled(pio0, sm_id, true);
}

void loop() {
    pio_sm_put_blocking(pio0, sm_id, (uint32_t)0b01010111 << 24);
    pio_sm_put_blocking(pio0, sm_id, (uint32_t)0b00000011 << 24);
    pio_sm_put_blocking(pio0, sm_id, (uint32_t)0b01100011 << 24);
    delay(2000);
}
Для примера передадим три байта в формате 0(старт)-5байт-11(стоп)
У нас данные хранятся в 32-разрядных регистрах. Если мы хотим расположить и передавать биты именно в такой последовательности, то нужно сдвинуть их влево, к MSB, на 32-8 = 24 позиции.
Код:
void loop() {
    pio_sm_put_blocking(pio0, sm_id, (uint32_t)0b01010111 << 24);
    pio_sm_put_blocking(pio0, sm_id, (uint32_t)0b00000011 << 24);
    pio_sm_put_blocking(pio0, sm_id, (uint32_t)0b01100011 << 24);
    delay(2000);
}
Вложение:
01.jpg
01.jpg [ 143.4 КБ | 97 просмотров ]
Длительность бита - ровно 22 мс.
Вложение:
02.jpg
02.jpg [ 140.31 КБ | 97 просмотров ]


Вложения:
BODO_MOD.rar [217.51 КБ]
2 скачивания
Вернуться к началу
 Заголовок сообщения: Re: RP2040
СообщениеДобавлено: 11 апр 2026, 09:51 
Не в сети

Зарегистрирован: 25 ноя 2024, 13:52
Сообщения: 97
Лаб.работа №4

Объединяем две предыдущие в одну.

RTTY-модем на пине 13 создаёт пачки импульсов,
С 13 пина через перемычку сигнал поступает на 14 пин.
14 пин является входом FSK-модулятора.
В зависимости от состояния пина 14, FSK-модулятор генерирует частоту на пине 15.
Основная программа направляет 11 байт, которые формируют слово RU0AOG.
Код:
#include "hardware/pio.h"
#include "hardware/clocks.h"

const float RTTY_BAUD = 45.45f;
const float PIO_FREQ = RTTY_BAUD * 2071.0f; 
const float PIO_FREQ_FSK = 117000.0f;

const uint  pin_rtty_out = 13;
const uint  pin_fsk_in   = 14;
const uint  pin_fsk_out  = 15;

PIO pio = pio0;
uint sm_fsk;
uint sm_id;

static const uint16_t rtty_modem_instructions[] = {
    0x80a0, // 0: pull block
    0xe047, // 1: set y, 7
    0x7401, // 2: out pins, 1 [20]
    0xe03f, // 3: set x, 31       
    0xbf42, // 4: nop [31]        
    0x0074, // 5: jmp x-- 4 [31]  
    0x0082, // 6: jmp y-- 2
    0xe001, // 7: set pins, 1    
};

static const struct pio_program rtty_prog = {
    .instructions = rtty_modem_instructions,
    .length = 8,
    .origin = -1,
};

static const uint16_t fsk_modulator_instructions[] = {
    0x00C7, // 0: jmp pin 7 
    0xfc01, // 1: set pins, 1 [28]
    0xbc42, // 2: nop [28]
    0xfc00, // 3: set pins, 0 [28]
    0xbb42, // 4: nop [27]
    0x0000, // 5: jmp 0        (Возврат на проверку пина)
    0x0001, // 6: jmp 1
    0xf801, // 7: set pins, 1 [24]
    0xb842, // 8: nop [24]
    0xf800, // 9: set pins, 0 [24]
    0xb742  // 10: nop [23]
};

static const struct pio_program fsk_mod_prog = {
    .instructions = fsk_modulator_instructions,
    .length = 11,
    .origin = -1,
};

void fsk_mod_setup() {
    uint offset = pio_add_program(pio, &fsk_mod_prog);
    sm_fsk = pio_claim_unused_sm(pio, true);

    pio_gpio_init(pio, pin_fsk_out);
    pio_gpio_init(pio, pin_fsk_in);
    pio_sm_set_consecutive_pindirs(pio, sm_fsk, pin_fsk_out, 1, true);
    pio_sm_set_consecutive_pindirs(pio, sm_fsk, pin_fsk_in, 1, false); 

    pio_sm_config c = pio_get_default_sm_config();
    sm_config_set_set_pins(&c, pin_fsk_out, 1);
    sm_config_set_jmp_pin(&c, pin_fsk_in); 
    
    float div = (float)clock_get_hz(clk_sys) / PIO_FREQ_FSK;
    sm_config_set_clkdiv(&c, div);
    
    // Явный wrap для FSK модулятора
    sm_config_set_wrap(&c, offset, offset + 10);
    
    pio_sm_init(pio, sm_fsk, offset, &c);
    pio_sm_set_enabled(pio, sm_fsk, true);
}

void rtty_modem_setup() {
    uint offset = pio_add_program(pio0, &rtty_prog);
    sm_id = pio_claim_unused_sm(pio0, true);

    // Твоя динамическая коррекция прыжков
    pio0->instr_mem[offset + 5] = 0x0040 | (offset + 4) | (31 << 8);
    pio0->instr_mem[offset + 6] = 0x0080 | (offset + 2);

    pio_gpio_init(pio0, pin_rtty_out);
    pio_sm_set_consecutive_pindirs(pio0, sm_id, pin_rtty_out, 1, true);

    pio_sm_config c = pio_get_default_sm_config();
    sm_config_set_out_pins(&c, pin_rtty_out, 1);
    sm_config_set_set_pins(&c, pin_rtty_out, 1);
    
    // Направление сдвига как просил: MSB (false), autopull: false (чтобы работал pull в коде)
    sm_config_set_out_shift(&c, false, false, 8); 
    
    sm_config_set_wrap(&c, offset, offset + 7);
    
    float div = (float)clock_get_hz(clk_sys) / PIO_FREQ;
    sm_config_set_clkdiv(&c, div);
    
    pio_sm_init(pio0, sm_id, offset, &c);
    pio_sm_set_enabled(pio0, sm_id, true);
}

void setup() {
    set_sys_clock_khz(125000, true);
    rtty_modem_setup();
    fsk_mod_setup();
}

void loop() {
    // MSB first: первым уйдет бит 31 (самый левый в твоем 0b...)
    pio_sm_put_blocking(pio0, sm_id, (uint32_t)0b00001011 << 24);
    pio_sm_put_blocking(pio0, sm_id, (uint32_t)0b00100011 << 24);
    pio_sm_put_blocking(pio0, sm_id, (uint32_t)0b01111111 << 24);
    pio_sm_put_blocking(pio0, sm_id, (uint32_t)0b00101011 << 24);
    pio_sm_put_blocking(pio0, sm_id, (uint32_t)0b01110011 << 24);
    pio_sm_put_blocking(pio0, sm_id, (uint32_t)0b01101111 << 24);
    pio_sm_put_blocking(pio0, sm_id, (uint32_t)0b00110111 << 24);
    pio_sm_put_blocking(pio0, sm_id, (uint32_t)0b01111111 << 24);
    pio_sm_put_blocking(pio0, sm_id, (uint32_t)0b01100011 << 24);
    pio_sm_put_blocking(pio0, sm_id, (uint32_t)0b00001111 << 24);
    pio_sm_put_blocking(pio0, sm_id, (uint32_t)0b00101111 << 24);
    delay(2000);
}
Вложение:
01.png
01.png [ 97.37 КБ | 91 просмотр ]
.
Вложение:
Sound002.mp3 [96.86 КБ]
2 скачивания
.
Вложение:
FSK_MOD.rar [143.25 КБ]
2 скачивания


Вернуться к началу
 Заголовок сообщения: Re: RP2040
СообщениеДобавлено: 11 апр 2026, 13:38 
Не в сети

Зарегистрирован: 25 ноя 2024, 13:52
Сообщения: 97
Лаб.работа №5

Дописываем кодировочные таблицы МТК-2 для латинский и русской раскладки.
Прописные и строчные буквы проще сразу прописать в таблицах.
Код:
#include "hardware/pio.h"
#include "hardware/clocks.h"

const float RTTY_BAUD = 45.45f;
const float PIO_FREQ = RTTY_BAUD * 2071.0f; 
const float PIO_FREQ_FSK = 117000.0f;

const uint  pin_rtty_out = 13;
const uint  pin_fsk_in   = 14;
const uint  pin_fsk_out  = 15;

PIO pio = pio0;
uint sm_fsk;
uint sm_id;

// Коды управления регистрами
#define MTK2_LAT 0x1F // 000 11111
#define MTK2_FIG 0x1B // 000 11011
#define MTK2_RUS 0x00 // 000 00000

// Структура для поиска символа
typedef struct {
    const char* s; // Строка вместо одного символа
    uint8_t code;
} mtk2_map_t;

// Латинский регистр
const mtk2_map_t table_lat[] = {
    {"A", 0x03}, {"B", 0x19}, {"C", 0x0E}, {"D", 0x09}, {"E", 0x01}, 
    {"F", 0x0D}, {"G", 0x1A}, {"H", 0x14}, {"I", 0x06}, {"J", 0x0B}, 
    {"K", 0x0F}, {"L", 0x12}, {"M", 0x1C}, {"N", 0x0C}, {"O", 0x18}, 
    {"P", 0x16}, {"Q", 0x17}, {"R", 0x0A}, {"S", 0x05}, {"T", 0x10}, 
    {"U", 0x07}, {"V", 0x1E}, {"W", 0x13}, {"X", 0x1D}, {"Y", 0x15}, 
    {"Z", 0x11},
    {"a", 0x03}, {"b", 0x19}, {"c", 0x0E}, {"d", 0x09}, {"e", 0x01}, 
    {"f", 0x0D}, {"g", 0x1A}, {"h", 0x14}, {"i", 0x06}, {"j", 0x0B}, 
    {"k", 0x0F}, {"l", 0x12}, {"m", 0x1C}, {"n", 0x0C}, {"o", 0x18}, 
    {"p", 0x16}, {"q", 0x17}, {"r", 0x0A}, {"s", 0x05}, {"t", 0x10}, 
    {"u", 0x07}, {"v", 0x1E}, {"w", 0x13}, {"x", 0x1D}, {"y", 0x15}, 
    {"z", 0x11},
    {" ", 0x04}, {"\n", 0x08}, {"\r", 0x02}, {NULL, 0}
};

// Цифровой регистр (ФИГ)
const mtk2_map_t table_fig[] = {
    {"-", 0x03}, {"?", 0x19}, {":", 0x0E}, {"3", 0x01}, {"Э", 0x0D},
    {"Ш", 0x1A}, {"Щ", 0x14}, {"8", 0x06}, {"Ю", 0x0B}, {"(", 0x0F},
    {")", 0x12}, {".", 0x1C}, {",", 0x0C}, {"9", 0x18}, {"0", 0x16},
    {"1", 0x17}, {"4", 0x0A}, {"'", 0x05}, {"5", 0x10}, {"7", 0x07},
    {"=", 0x1E}, {"2", 0x13}, {"/", 0x1D}, {"6", 0x15}, {"+", 0x11},
    {"э", 0x0D}, {"ш", 0x1A}, {"щ", 0x14}, {"ю", 0x0B}, {"Ч", 0x0A},
    {"ч", 0x0A},
    {NULL, 0}
};

// Русский регистр (РУС)
const mtk2_map_t table_rus[] = {
    {"А", 0x03}, {"Б", 0x19}, {"Ц", 0x0E}, {"Д", 0x09}, {"Е", 0x01}, 
    {"Ф", 0x0D}, {"Г", 0x1A}, {"Х", 0x14}, {"И", 0x06}, {"Й", 0x0B}, 
    {"К", 0x0F}, {"Л", 0x12}, {"М", 0x1C}, {"Н", 0x0C}, {"О", 0x18}, 
    {"П", 0x16}, {"Я", 0x17}, {"Р", 0x0A}, {"С", 0x05}, {"Т", 0x10}, 
    {"У", 0x07}, {"Ж", 0x1E}, {"В", 0x13}, {"Ь", 0x1D}, {"Ы", 0x15}, 
    {"З", 0x11}, {"Ъ", 0x1D}, {"Ё", 0x01},
    {"а", 0x03}, {"б", 0x19}, {"ц", 0x0E}, {"д", 0x09}, {"е", 0x01}, 
    {"ф", 0x0D}, {"г", 0x1A}, {"х", 0x14}, {"и", 0x06}, {"й", 0x0B}, 
    {"к", 0x0F}, {"л", 0x12}, {"м", 0x1C}, {"н", 0x0C}, {"о", 0x18}, 
    {"п", 0x16}, {"я", 0x17}, {"р", 0x0A}, {"с", 0x05}, {"т", 0x10}, 
    {"у", 0x07}, {"ж", 0x1E}, {"в", 0x13}, {"ь", 0x1D}, {"ы", 0x15}, 
    {"з", 0x11}, {"ъ", 0x1D}, {"ё", 0x01},
    {NULL, 0}
};

enum MTK2_STATE { LAT, FIG, RUS };
MTK2_STATE current_reg = LAT;

// Функция поиска последовательности байт в таблице
bool find_in_table(const mtk2_map_t* table, const char* s, uint8_t &code, int &len) {
    for (int i = 0; table[i].s != NULL; i++) {
        int l = strlen(table[i].s);
        if (strncmp(table[i].s, s, l) == 0) {
            code = table[i].code;
            len = l;
            return true;
        }
    }
    return false;
}

// Вспомогательная функция для упаковки 5-битного кода в 8-битный кадр
uint8_t pack_frame(uint8_t code) {
    // 1. Разворачиваем биты (Bit Reversal)
    uint8_t code_rev = 0;
    if (code & 0x01) code_rev |= 0x10;
    if (code & 0x02) code_rev |= 0x08;
    if (code & 0x04) code_rev |= 0x04;
    if (code & 0x08) code_rev |= 0x02;
    if (code & 0x10) code_rev |= 0x01;

    // 2. Упаковываем: Старт в 7-й бит (0), Данные в 6-2, Стопы в 1-0 (1)
    uint8_t frame = 0;
    frame |= (1 << 0);           // Стоп 1
    frame |= (1 << 1);           // Стоп 2
    frame |= (code_rev << 2);    // Данные
    frame |= (0 << 7);           // Старт
    return frame;
}

// Новая основная функция отправки строки
void send_rtty_string(const char* s) {
    if (!Serial) Serial.begin(115200);

    while (*s) {
        uint8_t code = 0;
        int char_len = 1;
        MTK2_STATE target_reg = current_reg;
        bool found = false;

        if (find_in_table(table_lat, s, code, char_len)) { target_reg = LAT; found = true; }
        else if (find_in_table(table_fig, s, code, char_len)) { target_reg = FIG; found = true; }
        else if (find_in_table(table_rus, s, code, char_len)) { target_reg = RUS; found = true; }

        if (found) {
            // Смена регистра
            if (target_reg != current_reg) {
                uint8_t reg_code = (target_reg == LAT) ? MTK2_LAT : (target_reg == FIG ? MTK2_FIG : MTK2_RUS);
                uint8_t reg_frame = pack_frame(reg_code);
                
                if (target_reg==LAT) Serial.print("[LAT]");
                if (target_reg==FIG) Serial.print("[FIG]");
                if (target_reg==RUS) Serial.print("[RUS]");
                Serial.print(" BIN: ");
                for (int i = 7; i >= 0; i--) Serial.print(bitRead(reg_frame, i));
                Serial.println();

                pio_sm_put_blocking(pio0, sm_id, (uint32_t)reg_frame << 24);
                current_reg = target_reg;
                delay(45); 
            }

            // Отправка символа
            uint8_t frame = pack_frame(code);

            Serial.print("SENDING '");
            for(int i=0; i<char_len; i++) Serial.print(s[i]);
            Serial.print("' FRAME: ");
            for (int i = 7; i >= 0; i--) Serial.print(bitRead(frame, i));
            Serial.println();

            pio_sm_put_blocking(pio0, sm_id, (uint32_t)frame << 24);
            s += char_len;
        } else {
            s++;
        }
        delay(45);
    }

    // --- ДОБАВЛЕНИЕ CR + LF В КОНЦЕ ПЕРЕДАЧИ ---
    uint8_t cr_frame = pack_frame(0x02); // Код Carriage Return
    uint8_t lf_frame = pack_frame(0x08); // Код Line Feed

    Serial.println("SENDING CR+LF...");
    pio_sm_put_blocking(pio0, sm_id, (uint32_t)cr_frame << 24);
    delay(45);
    pio_sm_put_blocking(pio0, sm_id, (uint32_t)lf_frame << 24);
    delay(45);
}



static const uint16_t rtty_modem_instructions[] = {
    0x80a0, // 0: pull block
    0xe047, // 1: set y, 7
    0x7401, // 2: out pins, 1 [20]
    0xe03f, // 3: set x, 31       
    0xbf42, // 4: nop [31]        
    0x0074, // 5: jmp x-- 4 [31]  
    0x0082, // 6: jmp y-- 2
    0xe001, // 7: set pins, 1    
};

static const struct pio_program rtty_prog = {
    .instructions = rtty_modem_instructions,
    .length = 8,
    .origin = -1,
};

static const uint16_t fsk_modulator_instructions[] = {
    0x00C7, // 0: jmp pin 7 
    0xfc01, // 1: set pins, 1 [28]
    0xbc42, // 2: nop [28]
    0xfc00, // 3: set pins, 0 [28]
    0xbb42, // 4: nop [27]
    0x0000, // 5: jmp 0        (Возврат на проверку пина)
    0x0001, // 6: jmp 1
    0xf801, // 7: set pins, 1 [24]
    0xb842, // 8: nop [24]
    0xf800, // 9: set pins, 0 [24]
    0xb742  // 10: nop [23]
};

static const struct pio_program fsk_mod_prog = {
    .instructions = fsk_modulator_instructions,
    .length = 11,
    .origin = -1,
};

void fsk_mod_setup() {
    uint offset = pio_add_program(pio, &fsk_mod_prog);
    sm_fsk = pio_claim_unused_sm(pio, true);

    pio_gpio_init(pio, pin_fsk_out);
    pio_gpio_init(pio, pin_fsk_in);
    pio_sm_set_consecutive_pindirs(pio, sm_fsk, pin_fsk_out, 1, true);
    pio_sm_set_consecutive_pindirs(pio, sm_fsk, pin_fsk_in, 1, false); 

    pio_sm_config c = pio_get_default_sm_config();
    sm_config_set_set_pins(&c, pin_fsk_out, 1);
    sm_config_set_jmp_pin(&c, pin_fsk_in); 
    
    float div = (float)clock_get_hz(clk_sys) / PIO_FREQ_FSK;
    sm_config_set_clkdiv(&c, div);
    
    // Явный wrap для FSK модулятора
    sm_config_set_wrap(&c, offset, offset + 10);
    
    pio_sm_init(pio, sm_fsk, offset, &c);
    pio_sm_set_enabled(pio, sm_fsk, true);
}

void rtty_modem_setup() {
    uint offset = pio_add_program(pio0, &rtty_prog);
    sm_id = pio_claim_unused_sm(pio0, true);

    // Твоя динамическая коррекция прыжков
    pio0->instr_mem[offset + 5] = 0x0040 | (offset + 4) | (31 << 8);
    pio0->instr_mem[offset + 6] = 0x0080 | (offset + 2);

    pio_gpio_init(pio0, pin_rtty_out);
    pio_sm_set_consecutive_pindirs(pio0, sm_id, pin_rtty_out, 1, true);

    pio_sm_config c = pio_get_default_sm_config();
    sm_config_set_out_pins(&c, pin_rtty_out, 1);
    sm_config_set_set_pins(&c, pin_rtty_out, 1);
    
    sm_config_set_out_shift(&c, false, false, 8); 
    
    sm_config_set_wrap(&c, offset, offset + 7);
    
    float div = (float)clock_get_hz(clk_sys) / PIO_FREQ;
    sm_config_set_clkdiv(&c, div);
    
    pio_sm_init(pio0, sm_id, offset, &c);
    pio_sm_set_enabled(pio0, sm_id, true);
}

void setup() {
    set_sys_clock_khz(125000, true);
    Serial.begin(115200);
    rtty_modem_setup();
    fsk_mod_setup();
}

void loop() {
    send_rtty_string("CQ CQ CQ DE RU0AOG ПРИВЕТ");
    send_rtty_string("Съешь ещё этих мягких французских булок, да выпей же чаю (русский)");
    send_rtty_string("The quick brown fox jumps over the lazy dog (english)");
    send_rtty_string("0123456789,+/-.=(:'?)");
    Serial.println("pause");
    delay(5000);
}
В порт происходит вывод передаваемых символов и кодов
Вложение:
02.jpg
02.jpg [ 161.49 КБ | 90 просмотров ]
.
Проверяем программой передачу символов.
Цифра 0 почему-то принимается как что-то непонятное, хотя код соответствует.
Вложение:
01.jpg
01.jpg [ 256.45 КБ | 90 просмотров ]
.
Вложение:
Sound002.mp3 [296.25 КБ]
2 скачивания
.
Вложение:
RTTY_GEN.rar [398.56 КБ]
3 скачивания


Вернуться к началу
 Заголовок сообщения: Re: RP2040
СообщениеДобавлено: 12 апр 2026, 05:47 
Не в сети

Зарегистрирован: 25 ноя 2024, 13:52
Сообщения: 97
Лаб.работа №6

Подключим и запустим генерацию на модуле Si5351.
Модуль питается от +5В через управляющий pnp транзистор. Управление на пине 16 (0-вкл, 1-выкл).

Не будем пользоваться сторонними библиотеками. Для ускорения посчитаем все команды заранее и впишем их в массив.
Код:
#include <Wire.h>

#define INVERT_OFF
#define SI5351_I2C_ADDR 0x60
#define RTTY_PIN        15 
#define PIN_SDA         18
#define PIN_SCL         19

const uint8_t REGS0[] = {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 DATA0[] = {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};

const uint8_t REGS_FREQ[]  = {0x2A, 0x2B, 0x2C, 0x2D, 0x2E, 0x2F, 0x30, 0x31, 0x10, 0x03}; 
const uint8_t DATA_SPACE[] = {0xB2, 0xAC, 0x00, 0x7B, 0x55, 0xD5, 0x11, 0xE4, 0x0F, 0x00};
const uint8_t DATA_MARK[]  = {0xB2, 0xD6, 0x00, 0x7B, 0x53, 0xDB, 0xA9, 0x1E, 0x0F, 0x00};

// Адаптированная функция записи для Wire1
void si5351_write_reg(uint8_t reg, uint8_t data) {
  Wire1.beginTransmission(SI5351_I2C_ADDR);
  Wire1.write(reg);
  Wire1.write(data);
  Wire1.endTransmission();
}

void SI_init() {
  Wire1.setSDA(PIN_SDA);
  Wire1.setSCL(PIN_SCL);
  Wire1.begin();
  for (int i = 0; i < 25; i++) {
    si5351_write_reg(REGS0[i], DATA0[i]);
  }
  write_STOP();
}

void write_SPACE() {
  // Для Space пишем регистры частоты
  for (int i = 0; i < 10; i++) {
    #ifdef INVERT_ON
      si5351_write_reg(REGS_FREQ[i], DATA_MARK[i]);
    #else
      si5351_write_reg(REGS_FREQ[i], DATA_SPACE[i]);
    #endif
  }
  digitalWrite(RTTY_PIN, LOW); // Физический пин индикации
}

void write_MARK() {
  for (int i = 0; i < 10; i++) {
    #ifdef INVERT_ON
      si5351_write_reg(REGS_FREQ[i], DATA_SPACE[i]);
    #else
      si5351_write_reg(REGS_FREQ[i], DATA_MARK[i]);
    #endif
  }
  digitalWrite(RTTY_PIN, HIGH);
}

void write_STOP() {
  si5351_write_reg(0x03, 0xFF); // Выключить все выходы
  si5351_write_reg(0x10, 0x80); // Power down CLK0
}

void SI_POWER_ON() {
    digitalWrite(16, LOW); // GP16 = 0
    delay(100);
    SI_init();
}

void SI_POWER_OFF() {
    digitalWrite(16, HIGH); // GP16 = 1
}

void setup() {
    pinMode(16, OUTPUT); // Пин GP16
    Serial.begin(115200);
}

void loop() {
    SI_POWER_ON();
    Serial.println("SI POWER ON");
    delay(5000);
    write_MARK();
    Serial.println("MARK");
    delay(5000);
    write_SPACE();
    Serial.println("SPACE");
    delay(5000);
    write_STOP();
    Serial.println("STOP AND SI POWER OFF");
    SI_POWER_OFF();
    delay(5000);
}
Вложение:
01.jpg
01.jpg [ 141.75 КБ | 84 просмотра ]
Вложение:
RF_GEN.rar [140.07 КБ]
3 скачивания


Вернуться к началу
 Заголовок сообщения: Re: RP2040
СообщениеДобавлено: 14 апр 2026, 08:10 
Не в сети

Зарегистрирован: 25 ноя 2024, 13:52
Сообщения: 97
Сейчас соединения на тестовой плате выглядят так:
Вложение:
BEACON_SCH_v0.JPG
BEACON_SCH_v0.JPG [ 181.09 КБ | 71 просмотр ]
.
Питание модуля на си5351 подаётся через транзистор V1. Модуль потребляет приличный ток, я его просто отключаю.
Во включенном состоянии на V1 падает 0,7 В.
Часы питаются от своей батарейки.
Резистор 13-14 - перемычка.
Выход с 15 пина заходил на тестовую оцифровку. Надо убрать.
Также с 15 пина сигнал подаётся на мини-джек для передачи на звуковую карту для анализа RTTY-программами.
.


Вложения:
2026-04-14.rar [134.39 КБ]
3 скачивания
Вернуться к началу
 Заголовок сообщения: Re: RP2040
СообщениеДобавлено: 15 апр 2026, 10:28 
Не в сети

Зарегистрирован: 25 ноя 2024, 13:52
Сообщения: 97
Лаб.работа №7

Мигаем светодиодом с периодом 1 сек.
Так как стандартными задержками тут не обойтись, создаём вложенные циклы
Код:
static const uint16_t blink_instructions[] = {
    0xff01, // 0: set pins, 1 [31]  1+31
    0xe03f, // 1: set x, 31         1
    0xbf42, // 2: nop [31]          
    0x1f42, // 3: jmp x-- 2 [31]    1+31 (32+32)*32 = 2048
    0xff00, // 4: set pins, 0 [31]  1+31
    0xe03f, // 5: set x, 31         1
    0xbf42, // 6: nop [31]          
    0x1f46, // 7: jmp x-- 6 [31]    1+31 (32+32)*32 = 2048
};
Код:
#include "hardware/pio.h"
#include "hardware/clocks.h"

// Оставляем ваш массив как есть, так как мы загрузим его по адресу 0
static const uint16_t blink_instructions[] = {
    0xff01, // 0: set pins, 1 [31]  1+31
    0xe03f, // 1: set x, 31         1
    0xbf42, // 2: nop [31]          
    0x1f42, // 3: jmp x-- 2 [31]    1+31 (32+32)*32 = 2048
    0xff00, // 4: set pins, 0 [31]  1+31
    0xe03f, // 5: set x, 31         1
    0xbf42, // 6: nop [31]          
    0x1f46, // 7: jmp x-- 6 [31]    1+31 (32+32)*32 = 2048
};
// всего в цикле (1+31+1+2048)*2 = 4162 такта

static const struct pio_program blink_prog = {
    .instructions = blink_instructions,
    .length = 8,
    .origin = 0, // Пытаемся заставить SDK положить программу по адресу 0
};

void setup() {
    const uint led_pin = 25; 
    PIO pio = pio0;

    // Полная очистка, чтобы гарантировать offset = 0
    pio_clear_instruction_memory(pio);
    
    // Загружаем программу
    uint offset = pio_add_program(pio, &blink_prog);
    
    uint sm = pio_claim_unused_sm(pio, true);
    
    // Получаем дефолтную конфигурацию
    pio_sm_config c = pio_get_default_sm_config();
    
    // Настройка пинов
    sm_config_set_set_pins(&c, led_pin, 1);
    pio_gpio_init(pio, led_pin);
    pio_sm_set_consecutive_pindirs(pio, sm, led_pin, 1, true);

    // Установка границ цикла (wrap)
    sm_config_set_wrap(&c, offset + 0, offset + 7);

    float div = (float)clock_get_hz(clk_sys) / (4162.0f);
    sm_config_set_clkdiv(&c, div);

    // Инициализация и запуск
    pio_sm_init(pio, sm, offset, &c);
    pio_sm_set_enabled(pio, sm, true);
}

void loop() {
    // Процессор свободен
}
также в экселе написал шаблон для перевода из ассемблерных мнемокодов в машинный код
Вложение:
asm.rar [11.49 КБ]
1 скачивание


Вложения:
LAB_07.rar [50.32 КБ]
2 скачивания
Вернуться к началу
 Заголовок сообщения: Re: RP2040
СообщениеДобавлено: 15 апр 2026, 15:40 
Не в сети

Зарегистрирован: 25 ноя 2024, 13:52
Сообщения: 97
Лаб.работа №8

Теперь также поморгаем, но через прерывание.
Код:
#include "hardware/pio.h"
#include "hardware/clocks.h"
#include "hardware/irq.h" // Нужно для работы с прерываниями

// Уточненный массив: добавили инструкции irq 0 (0xc000)
static const uint16_t blink_instructions[] = {
    0xff01, // 0: set pins, 1 [31]
    0xc000, // 1: irq 0             <-- Генерируем прерывание при включении
    0xe03f, // 2: set x, 31         
    0xbf42, // 3: nop [31]          
    0x1f43, // 4: jmp x-- 3 [31]    (Адрес прыжка сместился на 3)
    
    0xff00, // 5: set pins, 0 [31]
    0xc000, // 6: irq 0             <-- Генерируем прерывание при выключении
    0xe03f, // 7: set x, 31         
    0xbf42, // 8: nop [31]          
    0x1f48, // 9: jmp x-- 8 [31]    (Адрес прыжка сместился на 8)
};

static const struct pio_program blink_prog = {
    .instructions = blink_instructions,
    .length = 10, // Длина увеличилась
    .origin = 0,
};

const uint onboard_led = 25;

// Обработчик прерывания
void pio_irq_handler() {
    // Инвертируем состояние светодиода на 25 пине
    gpio_put(onboard_led, !gpio_get(onboard_led));
    
    // Очищаем флаг прерывания PIO, чтобы оно не вызывалось бесконечно
    pio0_hw->irq = 1; 
}

void setup() {
    const uint led_pin = 12; 
    PIO pio = pio0;

    // Настройка встроенного светодиода
    gpio_init(onboard_led);
    gpio_set_dir(onboard_led, GPIO_OUT);

    pio_clear_instruction_memory(pio);
    uint offset = pio_add_program(pio, &blink_prog);
    uint sm = pio_claim_unused_sm(pio, true);
    
    pio_sm_config c = pio_get_default_sm_config();
    sm_config_set_set_pins(&c, led_pin, 1);
    pio_gpio_init(pio, led_pin);
    pio_sm_set_consecutive_pindirs(pio, sm, led_pin, 1, true);

    // Установка границ цикла (теперь от 0 до 9)
    sm_config_set_wrap(&c, offset + 0, offset + 9);

    // Пересчитаем делитель (теперь в цикле (1+1+1+2048)*2 = 4102 такта)
    float div = (float)clock_get_hz(clk_sys) / (4102.0f);
    sm_config_set_clkdiv(&c, div);

    // --- Настройка прерывания на стороне CPU ---
    // Включаем прерывание PIO0_IRQ_0
    irq_set_exclusive_handler(PIO0_IRQ_0, pio_irq_handler);
    irq_set_enabled(PIO0_IRQ_0, true);
    // Разрешаем конкретному источнику (sm) вызывать прерывание
    pio_set_irq0_source_enabled(pio, pis_interrupt0, true);

    pio_sm_init(pio, sm, offset, &c);
    pio_sm_set_enabled(pio, sm, true);
}

void loop() {
    // Все происходит в прерывании
}


Вложения:
08.rar [50.67 КБ]
2 скачивания
Вернуться к началу
 Заголовок сообщения: Re: RP2040
СообщениеДобавлено: 15 апр 2026, 15:42 
Не в сети

Зарегистрирован: 25 ноя 2024, 13:52
Сообщения: 97
Лаб.работа №9

Разделим прерывания - на включение и на выключение
Код:
#include "hardware/pio.h"
#include "hardware/clocks.h"
#include "hardware/irq.h"

// Массив инструкций с двумя разными прерываниями
static const uint16_t blink_instructions[] = {
    0xff01, // 0: set pins, 1
    0xc000, // 1: irq 0             <-- Сигнал "Включено"
    0xe03f, // 2: set x, 31         
    0xbf42, // 3: nop          
    0x1f43, // 4: jmp x-- 3    
    
    0xff00, // 5: set pins, 0
    0xc001, // 6: irq 1             <-- Сигнал "Выключено"
    0xe03f, // 7: set x, 31         
    0xbf42, // 8: nop          
    0x1f48, // 9: jmp x-- 8    
};

static const struct pio_program blink_prog = {
    .instructions = blink_instructions,
    .length = 10,
    .origin = 0,
};

const uint onboard_led = 25;

// Единый обработчик для PIO0
void pio_irq_handler() {
    PIO pio = pio0;
    
    // Проверяем, какой IRQ сработал
    if (pio_interrupt_get(pio, 0)) {
        gpio_put(onboard_led, true);  // Зажигаем при включении пина 12
        pio_interrupt_clear(pio, 0);  // Очищаем флаг 0
    }
    
    if (pio_interrupt_get(pio, 1)) {
        gpio_put(onboard_led, false); // Гасим при выключении пина 12
        pio_interrupt_clear(pio, 1);  // Очищаем флаг 1
    }
}

void setup() {
    const uint led_pin = 12; 
    PIO pio = pio0;

    gpio_init(onboard_led);
    gpio_set_dir(onboard_led, GPIO_OUT);

    pio_clear_instruction_memory(pio);
    uint offset = pio_add_program(pio, &blink_prog);
    uint sm = pio_claim_unused_sm(pio, true);
    
    pio_sm_config c = pio_get_default_sm_config();
    sm_config_set_set_pins(&c, led_pin, 1);
    pio_gpio_init(pio, led_pin);
    pio_sm_set_consecutive_pindirs(pio, sm, led_pin, 1, true);
    sm_config_set_wrap(&c, offset + 0, offset + 9);

    float div = (float)clock_get_hz(clk_sys) / (4102.0f);
    sm_config_set_clkdiv(&c, div);

    // Настройка прерываний
    irq_set_exclusive_handler(PIO0_IRQ_0, pio_irq_handler);
    irq_set_enabled(PIO0_IRQ_0, true);
    
    // Разрешаем оба источника (index 0 и 1) для системного прерывания IRQ0
    pio_set_irq0_source_enabled(pio, pis_interrupt0, true);
    pio_set_irq0_source_enabled(pio, pis_interrupt1, true);

    pio_sm_init(pio, sm, offset, &c);
    pio_sm_set_enabled(pio, sm, true);
}

void loop() {
}


Вложения:
09.rar [50.52 КБ]
2 скачивания
Вернуться к началу
 Заголовок сообщения: Re: RP2040
СообщениеДобавлено: 16 апр 2026, 03:48 
Не в сети

Зарегистрирован: 25 ноя 2024, 13:52
Сообщения: 97
Лаб. работа № 10

Разделим стейт-машины (думаю лучше использовать этот термин вместо "конечного автомата"):
- нулевая стейт-машина (SM0) на пине 13 генерирует меандр с частотой 1 Гц (программа из лаб.работы № 7),
- первая стейт-машина (SM1) слушает состояние пина 14 и в соответствии с изменениями его состояния вызывает прерывание IRQ_0 с флагами irq 0 или irq 1.
Обработчик прерываний, привязанный к линии IRQ_0 блока PIO, проверяет флаг и включает или выключает встроенный светодиод.

У нас стоит перемычка между 13 и 14 пинами, поэтому светодиод будет мигать.
Код:
#include "hardware/pio.h"
#include "hardware/clocks.h"
#include "hardware/irq.h"

// --- Программа 1: Генератор (8 инструкций) ---
static uint16_t gen_instructions[] = {
    0xff01, // 0: set pins, 1 [31]
    0xe03f, // 1: set x, 31
    0xbf42, // 2: nop [31]
    0x0042, // 3: jmp x-- 2 [31]  <-- Мы поправим адрес '2' в setup
    0xff00, // 4: set pins, 0 [31]
    0xe03f, // 5: set x, 31
    0xbf42, // 6: nop [31]
    0x0046  // 7: jmp x-- 6 [31]  <-- Мы поправим адрес '6' в setup
};

// --- Программа 2: Монитор (4 инструкции) ---
static uint16_t mon_instructions[] = {
    0x20ae, // 0: wait 1 gpio 14 (это 0x2080 + 14 + 0x20 для режима gpio)
    0xc000, // 1: irq 0
    0x202e, // 2: wait 0 gpio 14 (это 0x2000 + 14 + 0x20 для режима gpio)
    0xc001  // 3: irq 1
    // wait 1 gpio X = 0x2080 | 0x20 | X
    // wait 0 gpio X = 0x2000 | 0x20 | X
    // 0x20 — это бит-флаг режима GPIO, а X — номер пина (0–31).
};

const uint onboard_led = 25;
const uint gen_pin = 13;
const uint mon_pin = 14;

void pio0_irq_handler() {
// Эта процедура выполняется ядром Cortex-M0+ каждый раз, 
// когда любая из стейт-машин блока pio0 поднимает флаг прерывания
// Указатель на эту процедуру закреплён в блоке setup
    if (pio_interrupt_get(pio0, 0)) {
        gpio_put(onboard_led, true);
        pio_interrupt_clear(pio0, 0);
    }
    if (pio_interrupt_get(pio0, 1)) {
        gpio_put(onboard_led, false);
        pio_interrupt_clear(pio0, 1);
    }
}

void setup() {
    PIO pio = pio0;

    gpio_init(onboard_led);
    gpio_set_dir(onboard_led, GPIO_OUT);
    pio_clear_instruction_memory(pio);

    // --- Настройка SM0 (Генератор) ---
    // Патчим jmp вручную перед загрузкой, чтобы не зависеть от функций encode
    // Команда jmp x--: 000 | Delay(5) | 010 (cond) | Address(5)
    // Мы предполагаем, что pio_add_program положит это по offset
    struct pio_program gen_prog = {gen_instructions, 8, -1};
    uint off_gen = pio_add_program(pio, &gen_prog);

    // Патчим адреса переходов напрямую в памяти инструкций
    pio->instr_mem[off_gen + 3] = 0x1f40 | ((off_gen + 2) & 0x1F); // jmp x-- (off+2) [31]
    pio->instr_mem[off_gen + 7] = 0x1f40 | ((off_gen + 6) & 0x1F); // jmp x-- (off+6) [31]

    uint sm_gen = pio_claim_unused_sm(pio, true);
    pio_sm_config c_gen = pio_get_default_sm_config();
    sm_config_set_set_pins(&c_gen, gen_pin, 1);
    pio_gpio_init(pio, gen_pin);
    pio_sm_set_consecutive_pindirs(pio, sm_gen, gen_pin, 1, true);
    sm_config_set_wrap(&c_gen, off_gen + 0, off_gen + 7);
    sm_config_set_clkdiv(&c_gen, (float)clock_get_hz(clk_sys) / 4162.0f);
    pio_sm_init(pio, sm_gen, off_gen, &c_gen);

    // --- Настройка SM1 (Монитор) ---
    struct pio_program mon_prog = {mon_instructions, 4, -1};
    uint off_mon = pio_add_program(pio, &mon_prog);
    uint sm_mon = pio_claim_unused_sm(pio, true);
    pio_sm_config c_mon = pio_get_default_sm_config();
    
    // Инициализируем GPIO как вход для PIO
    pio_gpio_init(pio, mon_pin);
    gpio_set_dir(mon_pin, GPIO_IN);
    
    // Важно: для wait gpio настройки sm_config_set_in_pins не обязательны,
    // так как мы указали тип источника "gpio" (абсолютный номер) в самой инструкции.

    sm_config_set_wrap(&c_mon, off_mon + 0, off_mon + 3);
    pio_sm_init(pio, sm_mon, off_mon, &c_mon);

    // --- Прерывания ---
    irq_set_exclusive_handler(PIO0_IRQ_0, pio0_irq_handler);    // привязка прерываний блока PIO0 к процедуре pio0_irq_handler
    irq_set_enabled(PIO0_IRQ_0, true);                          // активация NVIC. Теперь сигнал проходит от блока PIO к контроллеру прерываний
    pio_set_irq0_source_enabled(pio, pis_interrupt0, true);     // на физической линии IRQ0 блока PIO разрешить установку флага pis_interrupt0 (соответствует команде irq 0)
    pio_set_irq0_source_enabled(pio, pis_interrupt1, true);     // на физической линии IRQ0 блока PIO разрешить установку флага pis_interrupt1 (соответствует команде irq 1)

    pio_sm_set_enabled(pio, sm_gen, true);
    pio_sm_set_enabled(pio, sm_mon, true);
}

void loop() {}



Вложения:
10.rar [51.52 КБ]
1 скачивание
Вернуться к началу
Показать сообщения за:  Поле сортировки  
Начать новую тему  Ответить на тему  [ 44 сообщения ]  На страницу Пред. 1 2 3 4 5 След.

Часовой пояс: UTC


Кто сейчас на конференции

Сейчас этот форум просматривают: нет зарегистрированных пользователей и 0 гостей


Вы не можете начинать темы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете добавлять вложения

Найти:
Перейти:  
Создано на основе phpBB® Forum Software © phpBB Limited
Русская поддержка phpBB