#1 Re: Програмування Arduino » CH32V003 as arduino » 2026-07-30 21:54:37

Вирішив теж спробувати цей picorvd. Довелось обробити напильником, щоб зібрати під сучасним Arch.

По-перше, в pico_sdk_import.cmake замінив PicoSDK на 2.3.0, бо з 1.5.1 виникала помилка через версію cmake. Ще і з нею розбиратись не хотілось, тому просто бампнув тег на останній, там версію cmake уже пофіксили.

По-друге, в example/build замінив префікс тулчейна на riscv64-elf-, та шлях до хедерів newlib на /usr/risc64-elf/include, де воно в мене і лежить.

Також поправив blink.cpp, щоби блимало PC4, а не PD0 та PD4. Бо LED у мене на PC4, а на PD4 SWIO.
Прошилось і навіть щось запрацювало.

dimich пише:

А, бачу, воно вже з -O0 компілиться. Але ж агрумент все одно через регістр передається: https://godbolt.org/z/o5YYEjYEK

Тут трохи не додивився: аргумент-то передається в регістрі, але в самій функції потім кладеться в стек.
При blink зібраному з "-ggdb -Og" навіть значення аргумента показує правильно.

$ riscv32-elf-gdb ./bin/blink.elf 
Reading symbols from ./bin/blink.elf...
(gdb) target extended-remote /dev/ttyACM0
Remote debugging using /dev/ttyACM0
0x0000019e in busywait (x=x@entry=300000) at blink.cpp:8
8          for (volatile int i = 0; i < x; i++) {
(gdb) p x
$1 = 300000
(gdb) info frame
Stack level 0, frame at 0x200007f0:
 pc = 0xd28 in busywait (blink.cpp:8); saved pc = 0xdb4
 called by frame at 0x200007fc
 source language c++.
 Arglist at 0x200007f0, args: x=x@entry=300000
 Locals at 0x200007f0, Previous frame's sp is 0x200007f0
(gdb) bt
#0  0x00000d28 in busywait (x=x@entry=300000) at blink.cpp:8
#1  0x00000db4 in main () at blink.cpp:35

При зупинці по брекпоінту перестає, "Breakpoint 1, busywait (x=<error reading variable: Cannot access memory at address 0xfffffff0>) at blink.cpp:8"
А от якщо при збірці blink прибрати garbage collection секцій (при цьому розмір blink зростає майже до 4 кб), то початкове вхідне значення показує правильно, а поточне відповідає значенню регістра, в якому передавався аргумент:

(gdb) break 8
Breakpoint 1 at 0xd24: file blink.cpp, line 8.
Note: automatically using hardware breakpoints for read-only addresses.
(gdb) continue 
Continuing.

Breakpoint 1, busywait (x=32800, x@entry=300000) at blink.cpp:8
8          for (volatile int i = 0; i < x; i++) {
(gdb) info registers
ra             0xda4    0xda4 <main()+108>
sp             0x200007f0       0x200007f0
gp             0x20000800       0x20000800
tp             0x804e06a4       0x804e06a4
t0             0x1a0    416
t1             0x40014281       1073824385
t2             0x5b763111       1534472465
fp             0x0      0x0 <InterruptVectorDefault>
s1             0x40011000       1073811456
a0             0x8020   32800
a1             0x4002200c       1073881100
a2             0x8000d40        134221120
a3             0x50000  327680
a4             0x10040  65600
a5             0x90000  589824
pc             0xd2e    0xd2e <busywait(int)+12>

"monitor reset" працює, покрокове виконання працює.

#2 Re: Програмування Arduino » CH32V003 as arduino » 2026-07-29 21:46:47

jokeR пише:

Через файл gdbinit wink

А, ну якщо там є target remote / extended-remote, і gdb запускаєте з '-x gdbinit', то має зʼєднуватись.
Не знаю, чи лежить він там у вас як ~/.config/gdb/gdbinit і ваш gdb-multiarch автоматично виконує його при старті, чи потрібно в командному рядку кожний раз передавати.

jokeR пише:

Походу його не навчили цьому sad

Код для ресету там є: src/RVDebug.cpp:95. А

"monitor" command not supported by this target.

- це про команду "monitor", яка не supported, поки target не визначений. Тому логічно припустити, що gdb просто нікуди не підключений в цей момент.

jokeR пише:

а що debug не працює - незручно sad

По факту він-то працює. На брекпоінті зупинився ж якось. Регістри та машинний код показує? Ну а змусити значення змінних показувати, коли вони у регістрах - це навіть на рідній хостовій системі не завжди вдається.

#3 Re: Програмування Arduino » CH32V003 as arduino » 2026-07-29 20:18:51

jokeR пише:

У нас є gdb-multiarch, є ttyACM0

Ну gdb-multiarch має ж якось взнати, що є ttyACM0, на якому його будуть слухати.

jokeR пише:

є магічний пристій, що слухає ttyACM0 і смикає CH32V003 за SDIO.

Магічний пристрій і є монітор, який реалізує протокол gdb-сервера. В "класичній" конфігурації цю роль виконує openocd, який в свою чергу вже спілкується з залізом по іншом протоколу, наприклад, по JTAG.

#4 Re: Програмування Arduino » CH32V003 as arduino » 2026-07-29 18:12:29

jokeR пише:

ніт, "monitor" command not supported by this target.

А ви gdb з монітором зʼєднали?

After that run "gdb-multiarch {your_binary.elf}" and type "target extended-remote /dev/ttyACM0" to connect to the debugger (replace ttyACM0 with whatever port your Pico shows up as).

jokeR пише:

$CC -g -O0

Так, уже побачив, відредагував.

#5 Re: Програмування Arduino » CH32V003 as arduino » 2026-07-29 17:28:47

jokeR пише:

Трохи дивує що заливається не .bin, а .elf.

Gdb бере elf на вхід. З нього ж таблицю символів читає для дебага, і дебажні символи, якщо є, і цільову архітектуру для multiarch.
Сирий бінарик прошити можна командою restore.

jokeR пише:

Десь під капотом повинна бути магія objcopy?

Та яка там магія. Заголовок, секції з атрибутами, таблиця символів. Avrdude ж теж elf шʼє.

jokeR пише:

Ага, трохи роз"ясняється. picorvd - заливає прошивку правильно, але в кінці не робить reset.

А "monitor reset" працює?

jokeR пише:

ardulink, походу, робить reset через живлення wink

WCH-LinkE теж вміє живленням смикати. Корисно коли нема NRST.

jokeR пише:

Note: automatically using hardware breakpoints for read-only addresses.

Дивно, в README picorvd пишуть же:

The CH32V003 chip does not support any hardware breakpoints.

jokeR пише:

0x000001e4 in busywait (x=<error reading variable: Cannot access memory at address 0xfffffff4>) at blink.cpp:4

Скоріш за все той аргумент x зник ще на етапі компіляції. Але чому gdb в неіснуючу памʼять лізе, замість того щоб сказати value optimized out - то вже х.з. 
Взагалі, щоб більш-менш адекватний рантайм дебаг отримати, потрібно збирати з -Og, або хоча б -fno-omit-frame-pointer.
UPD: А, бачу, воно вже з -O0 компілиться. Але ж агрумент все одно через регістр передається: https://godbolt.org/z/o5YYEjYEK

#6 Re: Програмування Arduino » CH32V003 as arduino » 2026-07-28 20:06:13

jokeR пише:

blink наче заливає, але рівень на пінах не міняється.

На пінах цільового CH32? А якщо той же бінарик зашити заздалегідь робочим програматором?
Впевніться, що дивитесь на тому ж піні, яким цей blink дригає. На різних платах LED на різних пінах може бути, це ж не ардуіно. 

jokeR пише:

Що хотів сказати автор issue.. Не та версія компілятора? gdb? А яка потрібна?

А х.з., у нього треба питати. Мабуть тулчейн мається на увазі, бо в xpack і компілятор, і gdb.
Теоретично, на момент написання того комента уже був gdb 13-ї версії, а може й 14-ї. А platformio досі ставить 12.1.

jokeR пише:

Жаль що до кінця ту штуку не доробили sad

Я б не покладав сподівань на проект, в якому приклад blink лінкується з libgcc.a, що лежить в цьому ж репозиторії.

#7 Re: Програмування Arduino » CH32V003 as arduino » 2026-07-27 14:54:55

jokeer пише:

Няп тут про bootloader,  який переводиться в режим dfu підтяжкою одної ноги. Для 8-ногого контроллера 3 ноги віддати для програмування це по багатому wink

Ну 2 з них - UART, як і в ардуіні. Атмегу ж теж можна хоч по ISP, хоч одним проводом по debugWIRE прошивати, але не всі хотять додаткові провода, хотять бутлоадер і аплоад з IDE. В деяких випадках так зручніше.
Колись хотів детально розібратись, які там є доступні варіанти прошивки окрім SWIO, але руки не дійшли. З WCH-LinkE запрацювало, цього було достатньо. Але надибав такий опис: WCH CH32V003 Factory Bootloader - The Missing Manual.

#8 Re: Програмування Arduino » CH32V003 as arduino » 2026-07-26 22:46:17

jokeer пише:

Ну.. такоє.. якщо в мене є програматор, який по 3 проводах може залити bootloader.. і працювати як debugger.. навіщо заливати програму через uart і 4 проводи?

Мені здається, сенс приведених лінків у тому, що Ardulink-pio - не єдине можливе рішення працювати з CH32V003 при відсутності WCH-LinkE.
А з Ardulink-pio прошивщик хіба не пише у віртуальний UART, який по USB пише в USB-UART адаптер, який в свою чергу пише в атмегу, яка уже по 3х проводах взаємодіє з цільовим МК? Тут від ардуіно просто зручність, що USB-UART та контролер на одній платі.
Це якщо взяти МК, який реалізує USB девайс, то можна позбутись UART'а із ланцюжка. Але тоді прошивщик має вміти взаємодіяти з vendor-specific USB девайсом.

#9 Re: Програмування Arduino » CH32V003 as arduino » 2026-07-25 17:02:26

jokeer пише:

Тепер би ще придумати до чого приспособити той wch-link wink

Ну як мінімум як USB-UART адаптер має працювати smile А можна заморочитись і спробувати перешити його додатково під інший протокол, зробити 1-Wire / debugWIRE адаптер, чи I²C, чи повільний напівдуплексний SPI. Дивлячить на якому МК і як він там зроблений. А AVR ISP, мабуть, без хірургічного втручання не вийде, бо 4 лінії потрібно.

#10 Re: Програмування Arduino » CH32V003 as arduino » 2026-07-24 17:55:45

jokeR пише:

У 8 ногих дрібноконтролерів свій шарм wink Так, теж прошивається.

Наскільки розумію, кристал у всіх CH32V003 абсолютно однаковий, просто в різних корпусах.

#11 Re: Програмування Arduino » Arduino Pro 8 МГц і бібліотека Servo » 2026-07-24 17:49:40

AS31979 пише:

"Зовсім не зрозуміло, як це логіка роботи залежить від виду конструкції розгалуження" - легко

А, просто else забули.
Ну якщо Shag приймає тільки два значення, 1 і 2, то можна без другого if:

if (Shag == 1) {
    ...
} else {
} ...

Або просто в кінці гілки робити return;

AS31979 пише:

я так розумію це асемблер

Ні, там ніякого асемблера нема, тільки C++. Навіть ніяких особливостей від ++ не використовується, як C код він теж валідний.

#12 Re: Програмування Arduino » CH32V003 as arduino » 2026-07-24 16:08:05

jokeR пише:

CH32V003F4P6

Бавився з ними трохи, а також з восьминогими CH32V003J4M6. Правда, не через ардуіно фреймворк, а з ch32v003fun.
Якби їм ще зовнішній Vref зробили, взагалі ціни би їм не було. І засмутила відсутність SPI у J4M6.   

jokeR пише:

він же SWD, і, схоже, не можна його просто взяти і використати як GPIO.

Так, не можна. Мало того, якщо спробувати використати, можна залочити чіп у непрацездатний стан. І якщо F4P6 розлочити доволі просто, то щоб розлочити J4M6 без NRST піна довелось потанцювати з бубном.

jokeR пише:

Я купив wch-link, а треба wch-linkE wink

wch-linkE сам по собі на CH32V305 зроблений)

#13 Re: Програмування Arduino » Arduino Pro 8 МГц і бібліотека Servo » 2026-07-23 20:10:47

AS31979 пише:

виявилось що наступний імпульс формується відразу після закінчення формування попередн"ого.

А де ви обчислюєте значення для OCR після скидання піна, щоб наступне переривання виникало через потрібний проміжок часу?

Це якщо з одним перериванням, як в Servo, і як ви зараз робите. А можна з двома.
Щоб фаза для кожного канала була постійна, зробіть тайм-слот для кожного канала. Для 4 каналів рівномірно - по 5 мс виходить. На початку тайм-слота виставляєте пін у високий рівень та програмуйте OCR, в кінці тайм-слота скидайте пін у низький рівень.
Початок тайм слота - по перериванню COMPA, значення OCR1A визначає тривалість всього тайм-слота - 5 мс.  Кінець - по COMPB. Значення OCR1B визначає тривалість високого рівня.
Там іще є варіанти, але поки не буду ускладнювати.

AS31979 пише:

Знов довелося перейти на switch    case

Зовсім не зрозуміло, як це логіка роботи залежить від виду конструкції розгалуження. Щось там не так.

AS31979 пише:

Хто може ЛЮДСКОЮ мовою пояснити як працює цей код

4 канала, для кожного канала пін потрібно змінювати 2 раза. Виходить 4*2 = 8 станів.

Serv на кожному виклику циклічно приймає значення від 0 до 7: 0, 1, 2, 3, 4, 5, 6, 7, 0, 1, 2, 3, 4, 5, 6, 7, 0, ...

Із цього значення обчислюєм номер канала, від 0 до 3:

    uint8_t channel = idx / 2;

І номер крока, від 0 до 1 (по факту - інвертований стан піна).

    uint8_t state = idx % 2;

Їхні значення виходять такі

Serv   channel   state

 0        0        0
 1        0        1
 2        1        0
 3        1        1
 4        2        0
 5        2        1
 6        3        0
 7        3        1
 0        0        0
 1        0        1
 2        1        0
...

Виставляєм пін, що відповідає поточному каналу, у відповідний стан:

digitalWrite(pins[channel], !state);

Програмуєм OCR на наступну ітерацію:

OCR1A = ocr[channel][state];

Тривалість високого рівня на каналі k визначається значенням ocr[k][0] (аналогічно як у вашому коді Serv_1_1, Serv_2_1 і т.д.), тривалість низького рівня - ocr[k][1] (у вашому коді Serv_1_2, Serv_2_2 і т.д.).

#14 Re: Програмування Arduino » Arduino Pro 8 МГц і бібліотека Servo » 2026-07-23 17:32:57

AS31979 пише:

якщо я ще приблизно розумію як він працює, то самостійно змінити за потреби 100% не зможу!!!

Якщо що - питайте, поясню. Там лише C-шні масиви та арифметика.
Ваші окремі змінні Serv_1_1, Serv_1_2, Serv_2_1, Serv_1_2 і т.д. просто переїхали в масив. Тепер вони доступні як ocr[0][0], ocr[0][1], ocr[1][0], ocr[1][1] і т.д.

AS31979 пише:

№47 - може окремий if  і виконується швидше(я спочатку їх і використав), але   switch    case дозволив обійтись без goto...

Звідки там goto береться? У вас завжди лише одна гілка виконується, і нічого більше. Робіть return в кінці кожної гілки:

  switch (Serv) {
    case 1:
      switch (Shag) {
        case 1:
          digitalWrite(2, HIGH);
          OCR1A = Serv_1_1;,
          Shag=2;
          return;
        case 2:
          digitalWrite(2, LOW);
          OCR1A = Serv_1_2;
          Shag=1;
          Serv=2;
          return;
      }
    case 2:
      ...
    case 3:
      ...
    case 4:
      ...
  }

Але все одно, вважаю такий код - збочення.

AS31979 пише:

"потрібно оновлювати з вимкеними перериваннями" - детальніше можна, у поточній редакції  word Serv_1_1

word - це unsigned int, тобто 16 біт в AVR. 8-бітний процесор пише в 16-бітну змінну побайтово, двома інструкціями. Коли переривання виникає між цими двома інструкціями, змінна виявляється оновлена не повністю, і обробник переривання читає некоректне значення.
Щоб обробник гарантовано прочитав коректне значення, обробку переривань на момент запису потрібно вимкнути.
На рівні фреймворку:

noInterrupts();
Serv_1_1 = new_value;
interrupts();

Або на рівні avr-libc:

cli();
Serv_1_1 = new_value;
sei();

Або для універсального коду, який може виконуватись як в основному потоці, так і в контексті переривання:

uint8_t sreg = SREG;
cli();
Serv_1_1 = new_value;
SREG = sreg;

#15 Re: Програмування Arduino » Arduino Pro 8 МГц і бібліотека Servo » 2026-07-23 16:01:22

jokeR пише:

Правильні макроси спрощують життя.

Та з макросами теж можна намудрити, якщо зловживати. Хіба так багато місць у програмі де конфігурується таймер, чи потрібно перемикатись між декількома конфігураціями в рантаймі?
Якщо хочеться високорівневих абстракцій, то краще вже робити засобами C++. Ну то таке, якщо нема вимог до стилистики коду, то справа особистих вподобань.

#16 Re: Програмування Arduino » Arduino Pro 8 МГц і бібліотека Servo » 2026-07-22 21:35:38

Kino пише:

Всегда казалось, что сдвиг это команда LSL (LSR) с установкой флага переноса

Це вже деталі імплементації для конкретної архітектури. Я маю на увазі з точки зору стандарту мови програмування в загальному. Він нічого "не знає" про біти, флаги та інструкції.

#17 Re: Програмування Arduino » Arduino Pro 8 МГц і бібліотека Servo » 2026-07-22 21:10:38

Kino пише:

За стандарт не скажу, честно не знаю. Но помню книжка писала, что если не указано явно unsigned, то по умолчанию переменная всегда signed.

Саме так. А при зсуві signed:

The result of E1 << E2 is E1 left-shifted E2 bit positions; ... If E1 has a signed type and nonnegative value, and E1 × 2^E2 is representable in the result type, then that is the resulting value; otherwise, the behavior is undefined.

6.5.7 Bitwise shift operators

#18 Re: Програмування Arduino » Arduino Pro 8 МГц і бібліотека Servo » 2026-07-22 21:04:50

Kino пише:

Та блин, эти стандарты уже так разрослись и отличаются, что уже не уследишь. ))

Стандартів для мов програмування не так уже й багато. Але так, вендори заліза клепають свої фреймворки з велосипедами на костилях. Тут нічого не поробиш, се ля ві.

#19 Re: Програмування Arduino » Arduino Pro 8 МГц і бібліотека Servo » 2026-07-22 20:51:52

Kino пише:

та дурня. UL это просто unsigned long, т.е. захватывает 4 байта. Если я напишу 1ULL это будет восемь байт, то шо, я еще круче?

Суть не в long, а в unsigned. Поведінка (1 << 15) при 16-бітному int не визначена стандартом. Те ж саме з (1 << 31) на платформах з 32-бітним int.

#20 Re: Програмування Arduino » Arduino Pro 8 МГц і бібліотека Servo » 2026-07-22 20:46:05

Kino пише:

Не в либах дело. Например, для размещения данных во флеш, у gcc используется PROGMEM

В gcc є підтримка named address spaces (ISO/IEC DTR 18037), так що в C __flash працює (для читання генерується lpm). В arduino, яке базується на C++, такого не буде.

Kino пише:

Это же касается и асм вставок, надо писать именно так, как хочет конкретный компилятор, и пофиг что у другого по другому, они никогда не будут совместимы в этом плане.

Бо inline assembler не є частиною стандарту, це розширення конкретного компілятора.

#21 Re: Програмування Arduino » Arduino Pro 8 МГц і бібліотека Servo » 2026-07-22 20:30:46

Kino пише:

Посмотрел. Раскладывается в тот же #define bit(b) (1UL << (b)).

1UL. В avr-libc просто 1.

#22 Re: Програмування Arduino » Arduino Pro 8 МГц і бібліотека Servo » 2026-07-22 19:56:07

jokeR пише:

Зверніть увагу на макрос _BV, краще виглядає ніж (1 << CS11)

Kino пише:

Этот макрос для конкретного компилятора, а 1 << CS11 работает везде.

У ардуіни ще свій макрос bit() є в Arduino.h. Який, доречі, правильніший за (1 << x) та _BV() з точки зору стандарту.

#23 Re: Програмування Arduino » Arduino Pro 8 МГц і бібліотека Servo » 2026-07-22 19:37:55

AS31979 пише:

важливіше розуміти як воно працює, а не щоб було красиво.

Одне іншому не заважає, а навіть допомагає. "Красиво" - це не мета, а наслідок коректності, зрозумілості та ефективності коду.

AS31979 пише:

Це с"огодні в мене такі налаштування таймеру такі, а якщо завтра захочу встановити інші?
Так усе у одному місці і з пам"яткою "що за що відповіда"....

Так в чому проблема залишити все там само і так само, тільки записувати значення в регістр один раз замість чотирьох? 

Вище наведено приклади, як можна написати щоб було і зручно, і правильно.

AS31979 пише:

Мене більше турбує обробка преривання, до ц"ого часу я обходився оператором  if, і на комбінацію    switch    case   прейшов щоб не пхати у код   goto .

Так всі гілки однакові, відрізняються лише значення змінних. Навіщо їх дублювати?
Нумеруйте все з нуля, окрема змінна Shag стає непотрібною. Всі ваші if та case перетворюються на:

static const uint8_t pins[4] = { 2, 4, 7, 8 };
static volatile uint16_t ocr[4][2];

ISR ...
{
    uint8_t idx = Serv;
    Serv = (idx + 1) % 8;

    uint8_t channel = idx / 2;
    uint8_t state = idx % 2;

    digitalWrite(pins[channel], !state);
    OCR1A = ocr[channel][state];
}

Тільки не забувайте, що змінні, які читаються в обробнику переривань та більші за 8 біт, з основної програми потрібно оновлювати з вимкеними перериваннями. Ви ж тільки шматок коду показали, тому я не знаю, який там у вас тип у Serv_1_1 та інших.

#24 Re: Програмування Arduino » Arduino Pro 8 МГц і бібліотека Servo » 2026-07-22 14:43:30

jokeR пише:

Правильно мабуть

    TCCR1B |= (1 << CS10);
    TCCR1B &= ~(1 << CS11);
    TCCR1B |= (1 << CS12);

Не зовсім. Правильно, щоб значення в регістрі оновлювалось однією інструкцією. А для працюючого таймера правильніше спочатку зупинити його, сконфігурувати, потім запустити. Звісно, якщо потрібно залишити якісь біти незмінними, то прочитати поточне значення і застосувати маску.
Якщо ж виставляється чи скидається лише один біт в одному з нижніх 64 регістрів, то компілятор може заоптимізувати це в одну інструкцію sbi чи cbi.

jokeR пише:

Або макросів навернути wink

Або static inline функцію. Але це вже синтаксичний цукор.

#25 Re: Програмування Arduino » Arduino Pro 8 МГц і бібліотека Servo » 2026-07-22 13:47:28

jokeR пише:

Якщо розписати так ... то таки чесно виконує безсмислену операцію

Справа не в беззмістовній операції, а в тому, що якби було би, наприклад,

    TCCR1B |= (1 << CS10); 
    TCCR1B |= (0 << CS11);
    TCCR1B |= (1 << CS12);

то по закінчені цього коду таймер би уже натікав більше ніж треба.
Для покращення читабельності можна ще, наприклад, так:

    uint8_t tmp = (1 << WGM12);
    tmp |= (1 << CS10); 
    tmp |= (0 << CS11);
    tmp |= (1 << CS12);
    TCCR1B |= tmp;

Підвал форуму