Если ваш вопрос не влез ни в одну из вышеперечисленных тем, вам сюда.
Ответить

CH32V и лента WS2812

Вт май 16, 2023 11:05:43

Всем привет!

Пытаюсь реализовать управление WS2812 с RISC-V контроллера CH32V307 (потом перенесу код на CH32V003) по образу и подобию с руководства по STM32: здесь.

Тактирование 96 МГц, выдать пачку импульсов не удается, прерывания по DMA не выдаются. По логическому анализатору импульс выдается на сбросе.
Подскажите, что не так

Код под спойлером

Спойлерmain.c
Код:
/********************************** (C) COPYRIGHT *******************************
* File Name          : main.c
* Author             : WCH
* Version            : V1.0.0
* Date               : 2021/06/06
* Description        : Main program body.
*********************************************************************************
* Copyright (c) 2021 Nanjing Qinheng Microelectronics Co., Ltd.
* Attention: This software (modified or not) and binary are used for
* microcontroller manufactured by Nanjing Qinheng Microelectronics.
*******************************************************************************/

/*
 *@Note
 USART Print debugging routine:
 USART1_Tx(PA9).
 This example demonstrates using USART1(PA9) as a print debug port output.

*/

#include "debug.h"
#include "ws2812.h"

/* Global typedef */

/* Global define */

/* Global Variable */

//TIM_TypeDef timer_handle;
TIM_TimeBaseInitTypeDef time_base_init = {0};
TIM_OCInitTypeDef timer_output_cfg = {0};

DMA_InitTypeDef dma_leds = {0};

void Timer_PWM_Init();
void DMA_LEDs_Init();

/*********************************************************************
 * @fn      main
 *
 * @brief   Main program.
 *
 * @return  none
 */
int main(void)
{
   NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
   SystemCoreClockUpdate();
   Delay_Init();
   USART_Printf_Init(115200);   
   printf("SystemClk:%d\r\n",SystemCoreClock);
   printf( "ChipID:%08x\r\n", DBGMCU_GetCHIPID() );
   printf("This is printf example\r\n");

   Timer_PWM_Init();
   DMA_LEDs_Init();

//   while(DMA_GetFlagStatus(DMA1_FLAG_TC1) == RESET);

   ws2812_init(TIM1);

   ws2812_set_pixel(0, 0, 0, 0);
   ws2812_show();
   int i = 0;
   while(1)
    {
       printf("OK\r\n");
       Delay_Ms(1000);
       ws2812_set_pixel(0, i++, 0, 0);
       ws2812_show();
   }
}

// Init Timer for PWM (PA8)
void Timer_PWM_Init() {

    GPIO_InitTypeDef GPIO_PWMInitStructure={0};
    RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOA | RCC_APB2Periph_TIM1, ENABLE );

    GPIO_PWMInitStructure.GPIO_Pin = GPIO_Pin_8;
    GPIO_PWMInitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
    GPIO_PWMInitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init( GPIOA, &GPIO_PWMInitStructure );
//    GPIO_PinRemapConfig(AFIO_PCFR1_PD01_REMAP, DISABLE);

    time_base_init.TIM_ClockDivision = TIM_CKD_DIV1;
    time_base_init.TIM_Period = 126;
    time_base_init.TIM_CounterMode = TIM_CounterMode_Up;
    time_base_init.TIM_Prescaler = 0;
    time_base_init.TIM_RepetitionCounter = 0;

    TIM_TimeBaseInit(TIM1, &time_base_init);

    timer_output_cfg.TIM_OCMode = TIM_OCMode_PWM1;
    timer_output_cfg.TIM_OutputState = TIM_OutputState_Enable;
    timer_output_cfg.TIM_OCPolarity = TIM_OCPolarity_High;
//    timer_output_cfg.TIM_OCIdleState = 0;
//    timer_output_cfg.TIM_OCNIdleState = 0;
    timer_output_cfg.TIM_Pulse = 0;

//    TIM_DMAConfig(TIM1, TIM_DMABase, TIM_DMABurstLength)

    TIM_OC1Init(TIM1, &timer_output_cfg);
    TIM_CtrlPWMOutputs(TIM1, ENABLE );
    TIM_OC1PreloadConfig( TIM1, TIM_OCPreload_Disable );
    TIM_ARRPreloadConfig( TIM1, ENABLE );
    TIM_Cmd( TIM1, ENABLE );
}

void DMA_LEDs_Init() {
    RCC_AHBPeriphClockCmd( RCC_AHBPeriph_DMA1, ENABLE );

    NVIC_SetPriority(DMA1_Channel1_IRQn,0xE0);
    NVIC_EnableIRQ(DMA1_Channel1_IRQn);

    dma_leds.DMA_PeripheralBaseAddr = (u32)TIM1->DMAADR;
    dma_leds.DMA_MemoryBaseAddr = (u32)get_ws2812b_array_ptr();

    dma_leds.DMA_DIR = DMA_DIR_PeripheralDST;
    dma_leds.DMA_BufferSize = LED_ARRAY_LEN*2;
    dma_leds.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
    dma_leds.DMA_MemoryInc = DMA_MemoryInc_Enable;
    dma_leds.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;
    dma_leds.DMA_MemoryDataSize = DMA_PeripheralDataSize_HalfWord;
    dma_leds.DMA_Mode = DMA_Mode_Normal;
    dma_leds.DMA_Priority = DMA_Priority_High;
    dma_leds.DMA_M2M = DMA_M2M_Disable;
    DMA_Init(DMA1_Channel1, &dma_leds);
    DMA_ITConfig(DMA1_Channel1,DMA_IT_TC,ENABLE);
    DMA_ClearFlag(DMA1_FLAG_TC1);
    DMA_Cmd(DMA1_Channel1, ENABLE);
}

/*********************************************************************
 * @fn      DMA1_Channel1_IRQHandler
 *
 * @brief   This function DMA1 Channel1 exception.
 *
 * @return  none
 */
void DMA1_Channel1_IRQHandler(void) __attribute__((interrupt("WCH-Interrupt-fast")));
void DMA1_Channel1_IRQHandler(void)
{

    if(DMA_GetITStatus(DMA1_IT_TC1))
    {
//       TIM_Cmd(TIM1, DISABLE);
    //       TIM_DMACmd(TIM1, DMA1_Channel1, NewState)
       DMA_ClearITPendingBit(DMA1_IT_TC1|DMA1_IT_GL1|DMA1_IT_HT1);
    }
}




ws2812.c
Код:
/*
 * ws2812.c
 *
 *  Created on: May 4, 2023
 *      Author: dmitr
 */
#include "ws2812.h"

uint16_t BUF_DMA[LED_ARRAY_LEN] = {0};

TIM_TypeDef * timer_handle = NULL;

int ws2812_init(TIM_TypeDef * th)
{

    if (!th) return -1;
    else timer_handle = th;

    ws2812_clear_buffer();

    return 0;
}

void ws2812_clear_buffer() {
    for(int i=0;i<(LED_ARRAY_LEN-DELAY_LEN);i++) BUF_DMA[i] = LOW;
}

uint32_t * get_ws2812b_array_ptr() {
    return &BUF_DMA;
}

void byte_to_sequence(uint8_t byte_to_convert, uint16_t * seq) {
    for (int i = 7; i <= 0; i--) {
        if (((byte_to_convert>>i) & 1) == 0) seq[i] = LOW;
        else seq[i] = HIGH;
    }
}

void copy_sequence_to_position(uint32_t position, int sp_offset, uint16_t * seq, uint16_t * led_array) {
    if (position > LED_COUNT) position = LED_COUNT;
    if (sp_offset > 2) sp_offset = 2;
    if (sp_offset < 0) sp_offset = 0;

    for (int i = 0; i < 8; i++) led_array[position+8*sp_offset+i] = seq[i];
}

void ws2812_set_pixel(uint32_t pixel_num, uint8_t r, uint8_t g, uint8_t b) {
    if (pixel_num > LED_COUNT) pixel_num = LED_COUNT;

    uint16_t tmp[8];

    byte_to_sequence(r, &tmp);
    copy_sequence_to_position(pixel_num, 0, &tmp, &BUF_DMA);

    byte_to_sequence(g, &tmp);
    copy_sequence_to_position(pixel_num, 1, &tmp, &BUF_DMA);

    byte_to_sequence(b, &tmp);
    copy_sequence_to_position(pixel_num, 2, &tmp, &BUF_DMA);
}

void ws2812_show() {
    DMA_Cmd(DMA1_Channel1, ENABLE);
}



ws2812.h
Код:
/*
 * ws2812.h
 *
 *  Created on: May 4, 2023
 *      Author: dmitr
 */

#ifndef USER_WS2812_H_
#define USER_WS2812_H_

#include "stdio.h"
#include "stdint.h"
#include "ch32v30x.h"
#include "ch32v30x_tim.h"
#include "ch32v30x_dma.h"

//----------
#define DELAY_LEN          48
#define LED_COUNT          8
#define LED_ARRAY_LEN      DELAY_LEN + LED_COUNT*24
#define HIGH               0x8A3D // 54%
#define LOW                0x3AE1 // 23%

int ws2812_init(TIM_TypeDef * th);
uint32_t * get_ws2812b_array_ptr();

void ws2812_set_pixel(uint32_t pixel_num, uint8_t r, uint8_t g, uint8_t b);

void ws2812_clear_buffer();

void ws2812_show();


#endif /* USER_WS2812_H_ */



Добавлено after 42 minutes 54 seconds:
Настроил логический анализатор на большую частоту выборок. При настройке Pulse выдается ШИМ, норм
Но DMA не работает

Re: CH32V и лента WS2812

Ср май 17, 2023 08:51:29

TIM1 точно на DMA1 Chanel11 заведён?

СпойлерИзображение
изображение_2023-05-17_085109716.png
(44.84 KiB) Скачиваний: 81

Re: CH32V и лента WS2812

Ср май 17, 2023 13:05:45

Может у них тоже чертовщина всякая с запросами ПДП настраивается в SYSCFG? Ну или что у них там в качестве аналога.

Re: CH32V и лента WS2812

Ср май 17, 2023 13:07:02

Так, теперь прерывание приходит, но один раз

И принцип, я так понимаю, такой: на таймере надо включить выход запроса DMA с определенного канала, с которого выдается ШИМ, активировать сам DMA, написать обработчик, а связь между запросом DMA и каналом таймера задана уже в железе

Код обработчика прерываний DMA
Код:
void DMA1_Channel2_IRQHandler(void) __attribute__((interrupt("WCH-Interrupt-fast")));
void DMA1_Channel2_IRQHandler(void)
{

    if(DMA_GetITStatus(DMA1_IT_TC2) != RESET) {
        DMA_ClearITPendingBit(DMA1_IT_TC2);
    }

    if (TIM_GetITStatus(TIM1, TIM_IT_CC1) != RESET) {
        TIM_ClearITPendingBit(TIM1, TIM_IT_CC1);
    }

    // Вставить настройку таймера для нового запроса
//    time_base_init.TIM_Period = 126;
    TIM1->CNT = 0;


    DMA_Cmd(DMA1_Channel2, DISABLE);
    TIM_DMACmd(TIM1, TIM_DMA_CC1, DISABLE);
    TIM_Cmd(TIM1, DISABLE);
    TIM_ITConfig(TIM1, TIM_IT_CC1, DISABLE);

    TIM_CCxCmd(TIM1, TIM_Channel_1, TIM_CCx_Disable);
    x++;
}


Запуск вывода
Код:
void ws2812_show() {
    TIM_DMACmd(TIM1, TIM_DMA_CC1, ENABLE);
    TIM_CCxCmd(TIM1, TIM_Channel_1, TIM_CCx_Enable);
    TIM_Cmd(TIM1, ENABLE);
    NVIC_EnableIRQ(DMA1_Channel2_IRQn);
    DMA_Cmd(DMA1_Channel2, ENABLE);
}

Re: CH32V и лента WS2812

Ср май 17, 2023 17:30:47

А где вы его включаете, чтобы оно много раз происходило?

Re: CH32V и лента WS2812

Ср май 17, 2023 17:42:38

DMA должно включаться каждый раз, когда вызываю ws2812_show(), вызывается каждые 100 мс

Re: CH32V и лента WS2812

Чт май 18, 2023 11:57:34

Помогло от одноразовой отправки вот что: перед отправкой семплов сбрасываю счетчик отправленных данных DMA
DMA_SetCurrDataCounter(DMA1_Channel2, LED_ARRAY_LEN);

Re: CH32V и лента WS2812

Чт май 18, 2023 12:20:56

Только не сбрасываете, а задаёте количество передаваемых данных.

Re: CH32V и лента WS2812

Сб май 20, 2023 01:10:11

Теперь другая проблема: посылка не отправляется в нужном формате
Решил проверить работу ШИМ, заодно и подкорректировать значения ШИМ для 0 и 1.
В итоге, засовываю в регистр CH1CVR значение для низкого уровня, получаю такую картину:
https://disk.yandex.ru/i/-wFed6Js7Ke30g
Код для инициализации таймера:
Код:
// Init Timer for PWM (PA8)
void Timer_PWM_Init() {

    GPIO_InitTypeDef GPIO_PWMInitStructure={0};
    RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOA | RCC_APB2Periph_TIM1, ENABLE );

    GPIO_PWMInitStructure.GPIO_Pin = GPIO_Pin_8;
    GPIO_PWMInitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
    GPIO_PWMInitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init( GPIOA, &GPIO_PWMInitStructure );
    GPIO_PinRemapConfig(AFIO_PCFR1_TIM1_REMAP_FULLREMAP, ENABLE);

    time_base_init.TIM_ClockDivision = TIM_CKD_DIV4;
    time_base_init.TIM_Period = 99;
    time_base_init.TIM_CounterMode = TIM_CounterMode_Up;
    time_base_init.TIM_Prescaler = 0;
    time_base_init.TIM_RepetitionCounter = 0;

    TIM_TimeBaseInit(TIM1, &time_base_init);

    timer_output_cfg.TIM_OCMode = TIM_OCMode_PWM1;
    timer_output_cfg.TIM_OutputState = TIM_OutputState_Enable;
    timer_output_cfg.TIM_OCPolarity = TIM_OCPolarity_Low;
    timer_output_cfg.TIM_OCIdleState = 0;
//    timer_output_cfg.TIM_OCNIdleState = 0;
    timer_output_cfg.TIM_Pulse = 10; // Если выставлять больше, уровень будет резко меняться, в конечном итоге выставится сплошной уровень по выходу ШИМ. HIGH 0x8F5C LOW 0x47AE

//    TIM_DMAConfig(TIM1, TIM_DMABase_CCR1, TIM_DMABurstLength_1Transfer);
//    TIM_DMACmd(TIM1, TIM_DMA_CC1, DISABLE);

    TIM_OC1Init(TIM1, &timer_output_cfg);
    TIM_CtrlPWMOutputs(TIM1, ENABLE );
    TIM_OC1PreloadConfig( TIM1, TIM_OCPreload_Disable );
    TIM_ARRPreloadConfig( TIM1, ENABLE );
    TIM_CCxCmd(TIM1, TIM_Channel_1, TIM_CCx_Enable);
    TIM_ITConfig(TIM1, TIM_IT_CC1, ENABLE);
    NVIC_EnableIRQ(TIM1_CC_IRQn);

    TIM_Cmd( TIM1, ENABLE );
}

Re: CH32V и лента WS2812

Сб май 20, 2023 17:54:16

Здесь верно?
dma_leds.DMA_PeripheralBaseAddr = (u32)TIM1->DMAADR;

Re: CH32V и лента WS2812

Ср май 31, 2023 20:59:37

Здесь верно?
dma_leds.DMA_PeripheralBaseAddr = (u32)TIM1->DMAADR;

Поправил на адрес на CH1CVR для TIM1. Теперь судя по всему, посылки передаются, но не получается выдержать тайминги
Ответить