Перейти к содержанию

CH32V

  • записей
    8
  • комментариев
    12
  • просмотра
    3 064

zhevak

3 499 просмотров

Чем программировать CH32V

Смешной вопрос!

Чем вообще программируются STM32? У кого достаточно финансовых возможностей, тот покупает дорогие фирменные программаторы. Я, как и многие другие разработчики, использую китайские «свистки» по 150 рублей. (Это они раньше столько стоили. Сколько стоят сейчас — я не знаю. Уже давно не покупал. Для работы с STM32 я всё ещё пользуюсь свистком, купленным лет пять назад.) Беда, однако, в том, что ST-Link не подходит для работы с CH32V.

Ну, хорошо. А чем же тогда программировать эти китайские штучки?

Чем-чем — дак, почти таким же китайским «свистком», название которому WCH-LinkE.

wchlinke2023-07-26-09-21-17.png.2e4907bb3ee93c3c5b6123a53ae1ec21.png

Но я не стал заморачиваться только на «свисток», а прикупил полный набор — «свисток», отладочные платы и сами микросхемы.

В «суповой» набор за полторы тысячи рублей (если быть более точным — за 1515,69) входят — собственно, сам «свисток», две отладочных платы (с CH32V003 и с CH32V203) и по пять штук тех и других микросхем.

d0a1d0bdd0b8d0bcd0bed0ba-d18dd0bad180d0b0d0bdd0b0-d0b2-2023-03-17-14-31-15-1.png.4af199757a66d793a78bfcdef64040fe.png

Причём, что интересно, всё это «бохацтво» пришло в довольно-таки приличной упаковке, а образцы микросхем — так вообще были упакованы в пластиковые коробочки на подобие из-под ювелирных изделий или из-под наручных часов. В общем, мелочь — а приятно!

Программатор собран на базе микроконтроллера тоже RISC-V — CH32V305F8. (Характеристики этого микроконтроллера не смотрел, но думаю, что они будут повкуснее CH32V203.)

У меня есть предположение, что такой программатор можно повторить. Но… какой в этом смысл? Дешевле китайского-то сделать всё равно не получится. Быстрее и проще купить ещё один, если этот сдохнет.

 

<---

Я пока не совсем разобрался со спецификой блога. Я так и не понял, как публиковать в блоге другие свои статьи.

Видимо, их нужно размещать здесь же. Это немного странно. Но по мере освоения, я приведу блог в нормальное состояние.

Несколько последовательных по смыслу статей будут размещены на этом месте.

<---

CH32V003. Формирование временнЫх интервалов

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

Таймеры в STM32 и в CH32V по сравнению с другими микроконтроллерами (например, MSP430, ATMEGA и другими) сильно навороченные, и разобраться сходу, как с ними работать, -- довольно-таки трудно. По себе сужу.

Документация в интернете в основном представлена на английском языке. На русском тоже есть, но есть один момент. Документации по таймерам конкретно для CH32V нет. Хотя таймеры в CH32V и STM32 очень похоже, но состав, названия регистров, названия битов по отношению к STM32 несколько различаются. Поэтому у разработчиков возникают определённые трудности, которые выливаются в затягивание сроков разработки программ.

Представленный в статье пример помогает быстрее начать с таймером работать.

В микроконтроллерах CH32V реализованы два таймера -- таймер общего назначения (General Purpose)  TIM2 и продвинутый таймер (Advanced) TIM1. В примере используется таймер общего назначения TIM2, но представленный код пригоден и для продвинутого таймера TIM1.

У таймера много функций, которые он может выполнять. Начать освоения таймера лучше с самой простой функции -- формирование временнЫх промежутков. Что это значит?

Допустим, мы пишем программу, которая выполняет какие-то действия (например, измеряет температуру). Поскольку программа измеряет температуру значительно быстрее, чем температура обычно меняется, то измерять температуру каждую миллисекунду нет смысла. Допустим, что нам нужно измерять температуру с периодом один раз в секунду. Само же время измерения и время передачи полученного значения температуры во внешний мир (на LCD или по последовательному каналу в компьютер) составляет 10 мс.

В этом случае главный цикл программы будет выглядеть как-то так:

int main(void) 
{
  ...
  tim_init(); // Настраиваю таймер

  // Главный цикл программы
  while (1)
  {
    temperature = get_temperature(); // Измеряю температуру 
    send_value(temperature); // Передаю показания
    wait(); // Жду секунду 
  }
}

В функции tim_init() производится настройка таймера на формирование секундных промежутков времени, а функция wait() тупо останавливявает выполнение программы до начала следующего промежутка времени.

Вот, эти-то функции мы сейчас и рассмотрим более подробно. Начнём с функции tim_init().

Перед тем как взаимодействовать с таймером нужно разрешить его работу, или другими словами подать на него тактирование. Это делается одной командой:

RCC->APB1PCENR |= RCC_TIM2EN; // Включаю таймер

Следующим шагом нужно настроить работу таймера. Допустим, что тактовая частота SysClock, на которой работает ядро микроконтроллера, равно 24 МГц, а предделитель для шины мы не используем (то есть тактовая частота шины APB1 тоже 24 МГц). Тогда оставшийся код инициализации таймера будет выглядеть так:

TIM2->INTFR = 0;        // Предочистка
TIM2->PSC = 24000 - 1;  // Входная частота таймера = 1 кГц 
TIM2->ATRLR = 1000;     // Соответствует одной секунде 
TIM2->CTLR1 = TIM_CEN;  // Запускаю таймер в работу

У каждого таймера есть свой предделитель. Он делить входную частоту на заданное значение и потом подает её на счетчик таймера. В нашем случае мы записываем коэффициент деления 24000 в регистр предделителя (PSC). Это значит, что после предделителя частота, котораяубдет поступать на счётчик таймера, будет равна 1 кГц.

У каждого счётчика так же имеется регистр автозагрузки. Работа этого регистра зависит от направления счёта счетчика -- увеличивает ли счетчик свое значение или же уменьшает. Значение из этого регистра либо загружается в счётчик каждый раз при достижении счётчиком нулевого значения, либо наоборот -- при достижении счётчиком значения, равного записанному в регистре PSC, счётчик обнуляется. В обоих случая счётчик формирует событие UIF, которое мы и будем отслеживать в функции wait().

Код функции wait() ещё проще:

void wait(void) 
{
  while (!(TIM2->INTFR & TIM_UIF)) 
    ; // Ожидаю поднятия флага UIF

  TIM2->INTFR = 0; // Сбрасываю флаг
}

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

Еще раз отмечу, что не смотря на то, что программа, построенная по предложенному способу, будет вполне рабочей, делать так не надо. Код программы был приведён только в учебных целях. С чего-то же нужно начинать?

 

CH32V003. Генератор временнЫх интервалов

По жизни намного чаще, чем описанный в предыдущей статье формирователь задержки, требуется формирователь временнЫх отметок, или другими словами -- таймер. Таймер, в нашем контексте, -- это такая бестия, которая периодически с заданным интервалом прерывает выполнение основной программы.

Будем работать с таймером TIM2. Ранее я уже говорил, что периферия CH32V и периферия STM32 сильно совпадают. И это есть хорошо! Но, как обычно, есть нюансы!

Количество и состав регистров таймера TIM2 у CH32V и STM32 совпадает на 100 %, хотя названия регистров сильно расходятся. Например, регистр управления у STM32 называется CR1, в то время как у CH32V он носит имя CTLR1. Или, вот, ещё например, регистр генерации событий -- у STM32 он называется EGR, а у CH32V -- SWEVGR. Не знаю, зачем это было нужно делать китайцам, но по моему мнению они сделали неверный шаг.

Что же касается названий битов в регистрах, то в документации (на STM32 и на CH32V) они совпадают полностью. Но одно дело pdf-ка и совсем другое дело хэдерные файлы. Не знаю, насколько это оказалось дурным, но в данном случае ход китайцев мне понравился. Например, бит разрешения работы таймера у STM32 называется длинно -- TIM_CR1_CEN, а у китайцев этот же бит называется проще -- TIM_CEN. Во всяком случае мне показалось, что писать наименования "китайских" битов легче.

Сравните:

TIM2->CR1 = TIM_CR1_CEN;  // STM32

и

TIM2->CTLR1 = TIM_CEN;  // CH32V

 

Однако, давайте вернёмся к теме нарезки времени на кусочки определённой длительности.

Обработчик прерывания от таймера TIM2 должен выглядеть следующим образом:

__attribute__((interrupt("WCH-Interrupt-fast")))
void TIM2_Handler(void)
{
  TIM2->INTFR &= ~TIM_UIF;  // Очистить флаг прерывания

  ... // Выполнить какую-то полезную работу
}

В отличие от программного кода для STM32 в программах для CH32V нужно обязательно перед объявлением обработчика прерывания добавлять строку:

__attribute__((interrupt("WCH-Interrupt-fast")))

Ну и не забывайте, что один обработчик может обслуживать целую кучу родственных прерываний. Например, в этом обработчике обслуживается прерывание, которое возбуждается в следствие События Подновления таймера. Кроме этого прерывания таймер может возбуждать также прерывание при возникновении условия захвата, при возникновении условия сравнении.

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

__attribute__((interrupt("WCH-Interrupt-fast")))
void TIM2_Handler(void)
{
  if (((TIM2->DMAINTENR & TIM_UIE) != 0) && ((TIM2->INTFR & TIM_UIF) != 0))
  { // Это прерываение разрешено и флаг прерывания поднят
    TIM2->INTFR &= ~TIM_UIF;  // Очистить флаг прерывания

    ... // Выполнить какую-то полезную работу
  }

  ... // Обслужить другие прерывания от таймера
}

Но если в вашей программе используется только одно прерывание (как в нашем примере), то такую проверку можно и не выполнять.

С прерыванием разобрались. Теперь нужно написать код, который правильно настроит (инициализирует) таймер TIM2. Этот код выглядит так:

void init_tim2(void)
{
  RCC->APB1PCENR |= RCC_TIM2EN;  // Включаю тактирование модуля таймера
 
  NVIC_EnableIRQ(TIM2_IRQn);     // Разрешаю прерывания от таймера TIM2

  TIM2->PSC   = 24000 - 1;       // Предделитель, тактовая частота счётчика 1 кГц 
  TIM2->ATRLR = 50;              // Интервал перываний 50 мс
  TIM2->INTFR = 0;               // Сбрасываю все флаги
  TIM2->DMAINTENR = TIM_UIE;     // Разрешаю прерывание по событию "Подновление таймера"
  TIM2->CTLR1 = TIM_CEN;         // И наконец разрешаю работу таймера TIM2
}

В результате мы получили программу, которая 20 раз в секунду прерывает работу основного цикла программы.

Как видите, писать программу для CH32V не сложнее, чем для STM32.

 

Изменено пользователем zhevak

3 Комментария


Рекомендуемые комментарии

Цитата

Чем программировать CH32V

Жаль, этот аспект не рассмотрели. Теоретически, способов несколько, и у всех свои грабли.

1. Загрузчик через USB или UART. Существует виндовая софтина, довольно кривая и неудобная. То вообще контроллер не видит, то программировать отказывается, то вообще падает с ошибкой. Есть ли нормальные - запускаемые из линуксовой консоли и работющие стабильно - я пока не знаю.

2. SWD. Обычный Openocd не поддерживается, обычные программаторы тоже. Надо собирать специально патченный openocd, надо покупать специальный программатор.

Для сравнения, GigaDevice тоже выпускают RISC-V контроллеры gd32vf103 - так их и обычным программатором на ft2232 прошить можно (а с недавних пор даже на непатченном openocd) и даже обычным stm32flash.

Цитата

Программатор собран на базе микроконтроллера тоже RISC-V — CH32V305F8

Тоже находил прошивку под ch32v305, она даже залилась на самодельную отадочную плату с ch32v307 и определилась как wch-link. Что интересно, прошивка выглядит как *.bin файл, то есть не зашифрована. Правда, весит 64 кБ. Для программатора это запредельно много.

Цитата

Документации по таймерам конкретно для CH32V нет.

Пока глубоко не копал (вот в gd32vf потыкал) - но там же вроде один в один как в stm, даже регистры и биты в них на тех же местах, только называются по-другому. Какой еще документации не хватает?

P.S. А еще у RISC-V приятный ассемблер. Вот на этом надо студентов учить, а не на вырвиглазном x86.

Ссылка на комментарий
2 часа назад, COKPOWEHEU сказал:

Жаль, этот аспект не рассмотрели. Теоретически, способов несколько, и у всех свои грабли.

1. Загрузчик через USB или UART. Существует виндовая софтина, довольно кривая и неудобная. То вообще контроллер не видит, то программировать отказывается, то вообще падает с ошибкой. Есть ли нормальные - запускаемые из линуксовой консоли и работющие стабильно - я пока не знаю.

2. SWD. Обычный Openocd не поддерживается, обычные программаторы тоже. Надо собирать специально патченный openocd, надо покупать специальный программатор.

Для сравнения, GigaDevice тоже выпускают RISC-V контроллеры gd32vf103 - так их и обычным программатором на ft2232 прошить можно (а с недавних пор даже на непатченном openocd) и даже обычным stm32flash.

Тоже находил прошивку под ch32v305, она даже залилась на самодельную отадочную плату с ch32v307 и определилась как wch-link. Что интересно, прошивка выглядит как *.bin файл, то есть не зашифрована. Правда, весит 64 кБ. Для программатора это запредельно много.

Пока глубоко не копал (вот в gd32vf потыкал) - но там же вроде один в один как в stm, даже регистры и биты в них на тех же местах, только называются по-другому. Какой еще документации не хватает?

P.S. А еще у RISC-V приятный ассемблер. Вот на этом надо студентов учить, а не на вырвиглазном x86.

 

Цитата

Жаль, этот аспект не рассмотрели.

(Имеется ввиду -- чем программировать CH32V.)

Я через USART заливал прошивку наверно раза два. И делал я это в Линуксе из RivaStudio (или как она там у них правильно называется). Из консоли заливать через USART не пробовал, поскольку как-то сразу перешёл на работы с МК через SWIO. Опятя же в Линуксе. Ну, то есть, в Линуксе работаь с МК принципиально возможно. Я ж в Линуксе работаю.

Другое дело, что я не получил достаточно опыта заливки бинарника через USART, чтобы на эту тему что-то умное сказать. Пусть это сделают те, кто лучше меня разбирается в вопросе.

 

Цитата

Надо собирать специально патченный openocd, надо покупать специальный программатор.

Да. Это пока так.

Но со временем, я в этом уверен, появится народный CHLink. Свято место пусто не бывает. Если фирма WCH сохранит низкие цены на свои камни, то они неизбежно войдут в арсенал разработчиков со всеми вытекающими последствиями.

Про "фирменный" (от фирмы WCH) OpenOCD я ещё просто не успел здесь разместить материал. Я только-только начал перетаскивать на cxem.net свои статьи с wordpress. Это только первые две с половиной статьи, которые я по незнанию специфики cxem.net выполнил как одну. Со временем я постараюсь сюда перетащить и другие свои статьи.

Цитата

Какой еще документации не хватает?

Документации конкретно по CV32V. Спасает положение то, что периферия совпадает с периферией STM32F. Китайская документация по CH32V -- не полная (в сравнении с составом документации на STM32), написана таким корявым языком, что "прям, ой, мама!". Ну и до кучи там полно чисто технических ошибок. Я об этом уже сказал в своей статье.

Изменено пользователем zhevak
Ссылка на комментарий
В 29.08.2023 в 17:30, zhevak сказал:

Я через USART заливал прошивку наверно раза два.

Я таким способом пользовался как основным при работе с gd32vf103. Под конец даже полноценный программатор-отладчик собрал. То есть не просто переходник на UART, а на два UART-а (один для программирования, второй для отладки) плюс выводы для reset и boot0. Вот, если интересно, полная версия https://karakatitsariscv.github.io/Karakatitsa.html Кстати, тут есть и про makefile для прошивки, в том числе удаленной.

И урезанная версия, зато с "флешкой", на которой хранятся ее же собственные исходники: https://habr.com/ru/articles/746704/

Ну вот лень было собирать патченный openocd :)

В 29.08.2023 в 17:30, zhevak сказал:

Но со временем, я в этом уверен, появится народный CHLink

Скорее уж допилят сам openocd чтобы через какую-нибудь ft2232 работало. На это вся надежда.

А на счет "народного" - если прошивки для ch32v203 и ch549 (или как его там) доступны, а цены невелики - откуда взяться конкуренции.

В 29.08.2023 в 17:30, zhevak сказал:

Документации конкретно по CV32V.

Так какой именно документации-то не хватает? Описание ядра вроде есть прямо на сайте, QingKeV4 Processor Manual. Описание регистров периферии в рефмане. Глубоко я его, конечно, не копал, но простейший блинк на ассемблере запустился вообще без проблем.

В 29.08.2023 в 17:30, zhevak сказал:

в сравнении с составом документации на STM32

Не переоценивайте документацию от ST. Когда я пытался разобраться как в stm32l151 запустить тачсенсор, как раз на это наткнулся. Информации вообще ноль. Даже через Куб пытался сгенерить пример - так он тоже не справился. Или с USB и тамошними кошмарными регистрами.

Впрочем, кто знает, не упоролись ли китайцы из WCH еще сильнее...

Ссылка на комментарий

Присоединяйтесь к обсуждению

Вы публикуете как гость. Если у вас есть аккаунт, авторизуйтесь, чтобы опубликовать от имени своего аккаунта.
Примечание: Ваш пост будет проверен модератором, прежде чем станет видимым.

Гость
Unfortunately, your content contains terms that we do not allow. Please edit your content to remove the highlighted words below.
Добавить комментарий...

×   Вставлено с форматированием.   Восстановить форматирование

  Разрешено использовать не более 75 эмодзи.

×   Ваша ссылка была автоматически встроена.   Отображать как обычную ссылку

×   Ваш предыдущий контент был восстановлен.   Очистить редактор

×   Вы не можете вставлять изображения напрямую. Загружайте или вставляйте изображения по ссылке.

Загрузка...

×
×
  • Создать...