GD32E230C8+WS2812+PWM

GD32E230C8+WS2812+TIMER14_CH0+PWM:

提示:pwm

灯光控制:

  • WS2812
  • PWM
  • GD32

参考大佬的代码(链接: link)

1表现问题:

提示:偶发pwm启动,第一个字节数据周期不够。灯光控制随机亮度减弱。导致灯光闪烁。

2解决办法:

解决办法:在发送前多加一个24bit复位数据。
uint16_t Pixel_Buf[Pixel_NUM + Reset_load_time][RGB_DATA_SIZE]; //RGB灯传输数据;多于的2*24bit是刷新用,所有数据=rest+数据+rest;

3 ws2812b.c代码:

展示代码`。

#include "ws2812b.h"
#include "systick.h"
#include "gd32e23x_gpio.h"
//查手册
#define TIMER14_CH0CV_ADDR       (TIMER14+0x34)                    /* CH0 */
#define TIMER14_CH1CV_ADDR       (TIMER14+0x38)                    /* CH1 */
//需要输出的io
#define WS2812_PIN_PORT GPIOA
#define WS2812_PIN_NUM GPIO_PIN_2
/*高低电平的占空比*/
#define CODE_1       (59)       //1码定时器计数次数
#define CODE_0       (28)       //0码定时器计数次数
#define Pixel_NUM		(40) //LED数量

#define Reset_load_time     (2)                   //rest时间
#define RGB_DATA_SIZE       (24)                  //一个灯24bit
uint16_t Pixel_Buf[Pixel_NUM + Reset_load_time][RGB_DATA_SIZE]; //RGB灯传输数据;多于的2*24bit是刷新用,所有数据=rest+数据+rest;

/*LED三原色结构体*/
typedef struct
{
    uint8_t R;
    uint8_t G;
    uint8_t B;
} RGB_Color_TypeDef;

typedef enum
{
    RED_COLOR = 0,
    GREEN_COLOR,
    BLUE_COLOR,
    SKY_COLOR,
    MAGENTA_COLOR,

    YELLOW_COLOR,
    WHITE_COLOR,
    BLACK_COLOR,

    OEANGE_COLOR,
    Max_COLOR
} rgb_color;

/*Some Static Colors------------------------------*/
const RGB_Color_TypeDef   Color_TypeDef_list[Max_COLOR + 1] =
{
    {255, 0, 0}, 				//RED
    {0, 255, 0},        //GREEN
    {0, 0, 255},        //BLUE
    {0, 255, 255},      //SKY
    {255, 0, 255},      //MAGENTA

    {255, 255, 0},      //YELLOW
    {255, 255, 255},    //WHITE
    {0, 0, 0},          //BLACK
    {152, 251, 152},    //OEANGE
    {255, 165, 0}      //TEST
};
//配置pwm定时器
void ws2812_timer_pwm_init(void)
{
    timer_oc_parameter_struct timer_ocintpara;
    timer_parameter_struct timer_initpara;
    rcu_periph_clock_enable(RCU_TIMER14);
    timer_deinit(TIMER14);
    /* TIMER14 配置 */
    timer_initpara.prescaler         = 0;//不分频,使用主频72mhz
    timer_initpara.alignedmode       = TIMER_COUNTER_EDGE;
    timer_initpara.counterdirection  = TIMER_COUNTER_UP;
    timer_initpara.period            = 89;//频率周期
    timer_initpara.clockdivision     = TIMER_CKDIV_DIV1;
    timer_initpara.repetitioncounter = 0;
    timer_init(TIMER14, &timer_initpara);
    /*TIMER14 配置 PWM mode */
    timer_ocintpara.outputstate  = TIMER_CCX_ENABLE;
    timer_ocintpara.outputnstate = TIMER_CCXN_DISABLE;
    timer_ocintpara.ocpolarity   = TIMER_OC_POLARITY_HIGH;
    timer_ocintpara.ocnpolarity  = TIMER_OCN_POLARITY_HIGH;
    timer_ocintpara.ocidlestate  = TIMER_OC_IDLE_STATE_HIGH;
    timer_ocintpara.ocnidlestate = TIMER_OCN_IDLE_STATE_LOW;
    /* TINER14_CH0 输出配置 */
    timer_channel_output_config(TIMER14, TIMER_CH_0, &timer_ocintpara);
    timer_channel_output_pulse_value_config(TIMER14, TIMER_CH_0, 0);
    timer_channel_output_mode_config(TIMER14, TIMER_CH_0, TIMER_OC_MODE_PWM0);
    timer_channel_output_shadow_config(TIMER14, TIMER_CH_0, TIMER_OC_SHADOW_DISABLE);
    timer_primary_output_config(TIMER14, ENABLE);
    timer_dma_transfer_config(TIMER14, TIMER_DMACFG_DMATA_CH0CV, TIMER_DMACFG_DMATC_4TRANSFER);
    timer_dma_enable(TIMER14, TIMER_DMA_UPD);//注意这里需要选择TIMER_DMA_UPD
    /* TIMER14 重装载使能 */
    timer_auto_reload_shadow_enable(TIMER14);
    /* TIMER14 使能 */
    timer_enable(TIMER14);
}
//配置dma
static void ws2812_dma_init(void)
{
    dma_parameter_struct dma_init_struct;
    /* enable DMA clock */
    rcu_periph_clock_enable(RCU_DMA);
    /* DMA channel1 initialize */
    dma_deinit(DMA_CH4);
    dma_init_struct.direction    = DMA_MEMORY_TO_PERIPHERAL;//内存到外设
    dma_init_struct.memory_addr  = (uint32_t)Pixel_Buf;//内存数据基地址(LED灯数组)
    dma_init_struct.memory_inc   = DMA_MEMORY_INCREASE_ENABLE;//内存地址自增
    dma_init_struct.memory_width = DMA_MEMORY_WIDTH_16BIT;//存储器数据传输宽度
    dma_init_struct.number       = (Pixel_NUM + Reset_load_time) * RGB_DATA_SIZE; //dma通道传输数据数量
    dma_init_struct.periph_addr  = (uint32_t)TIMER14_CH0CV_ADDR;//外设基地址
    dma_init_struct.periph_inc   = DMA_PERIPH_INCREASE_DISABLE;//外设地址自增关闭
    dma_init_struct.periph_width = DMA_PERIPHERAL_WIDTH_16BIT;//外设数据传输宽度
    dma_init_struct.priority     = DMA_PRIORITY_HIGH;//优先级
    dma_init(DMA_CH4, &dma_init_struct);
    /* configure DMA mode */
    dma_circulation_disable(DMA_CH4);//dma循环模式禁止
    dma_memory_to_memory_disable(DMA_CH4);//存储器到存储器传输禁止
    /* enable DMA channel1 */
    dma_channel_enable(DMA_CH4);//使能dma通道4
}
//初始化io口
static void ws2812_gpio_init(void)
{
    rcu_periph_clock_enable(RCU_GPIOA);
    gpio_mode_set(WS2812_PIN_PORT, GPIO_MODE_AF, GPIO_PUPD_NONE, WS2812_PIN_NUM);
    gpio_output_options_set(WS2812_PIN_PORT, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ, WS2812_PIN_NUM);
}
//初始化
void ws2812_init(void)
{
    ws2812_gpio_init();
    ws2812_timer_pwm_init();
    ws2812_dma_init();
}
//数据头和尾巴各自加一个reset
static void ws2812_reset_load(void)
{
    uint8_t i;

    for(i = 0; i < RGB_DATA_SIZE; i++)
    {
        Pixel_Buf[Pixel_NUM + 1][i] = 0;
        Pixel_Buf[0][i] = 0;
    }
}
/*
功能:返回传参颜色的结构体
*/
static RGB_Color_TypeDef ws2812_return_color_type(uint8_t color)
{
    return Color_TypeDef_list[color];
}

/*
功能:设定单个RGB LED的颜色,把结构体中RGB的24BIT转换为0码和1码
参数:LedId为LED序号,Color:定义的颜色结构体
*/
static void ws2812_rgb_set_color_buf(uint8_t LedId, RGB_Color_TypeDef Color)
{
    uint8_t i;

    if(LedId > Pixel_NUM)return; //avoid overflow 防止写入ID大于LED总数

    for(i = 0; i < 8; i++)
    {
        Pixel_Buf[LedId][i]   = ((Color.G & (1 << (7 - i))) ? (CODE_1) : CODE_0); //数组某一行0~7转化存放G
    }

    for(i = 8; i < 16; i++)
    {
        Pixel_Buf[LedId][i]  = ((Color.R & (1 << (15 - i))) ? (CODE_1) : CODE_0); //数组某一行8~15转化存放R
    }

    for(i = 16; i < 24; i++)
    {
        Pixel_Buf[LedId][i] = ((Color.B & (1 << (23 - i))) ? (CODE_1) : CODE_0); //数组某一行16~23转化存放B
    }
}
/*
功能:发送数组
*/
static void ws2812_rgb_send_array(void)
{
    while(dma_flag_get(DMA_CH4, DMA_INTF_FTFIF) == RESET);

    dma_flag_clear(DMA_CH4, DMA_INTC_FTFIFC);
    dma_channel_disable(DMA_CH4);
    dma_transfer_number_config(DMA_CH4, (Pixel_NUM + Reset_load_time) * 24);
    dma_channel_enable(DMA_CH4);
}

/****************************************************************
           彩灯设置函数
		n: 第几个灯(设置范围1-LED_NUM)
		g: 绿色值 (范围0-255)
		r: 红色值 (范围0-255)
		b: 蓝色值 (范围0-255)
		l: 亮度值 (范围0-100)

		例如:WS2812B_Set_Color_Light(2,0,60,80,20);
		功能:设置第2颗灯的绿色值0,红色值60,蓝色值80,亮度20
		例如:WS2812B_Set_Color_Light(5,RED,50);
		功能:设置第5颗灯为红色,亮度50
*****************************************************************/
void WS2812B_Set_Color_Light(uint8_t n, uint16_t color, uint8_t light)
{
    RGB_Color_TypeDef breathe_type = {0};
    RGB_Color_TypeDef color_type = ws2812_return_color_type(color);

    // 计算当前亮度下的颜色值
    if(light > 100)
    {
        light = 100;
    }

    breathe_type.R = (color_type.R * light) / 100;
    breathe_type.G = (color_type.G * light) / 100;
    breathe_type.B = (color_type.B * light) / 100;
    ws2812_rgb_set_color_buf(n + 1, breathe_type);//此处加1,跳过最开始的reset
}

//调用RGB_SetOne_Color函数,完成对多个LED的颜色设置。
void RGB_SetMore_Color(uint8_t head, uint8_t heal, rgb_color color)
{
    uint8_t i = 0;

    for(i = head; i <= heal; i++)
    {
        WS2812B_Set_Color_Light(i, color, 100) ;
    }
}
/****************************************************************
           彩灯设置生效函数
*****************************************************************/

void WS2812B_Send_All()
{
    ws2812_reset_load();
    ws2812_rgb_send_array();
}
//百分比亮灯
void WS2812B_Set_percent_Light(uint8_t place, rgb_color COLOR, uint8_t Light)
{
    //不亮的灯(黑色)
    RGB_SetMore_Color(0, Pixel_NUM, BLACK_COLOR);

    //准备数据
    for(int i = 0; i <= Pixel_NUM * place / 100; i ++)
    {
        WS2812B_Set_Color_Light(i, COLOR, Light);
    }

    //亮灯
    WS2812B_Send_All();
}
//单个扫描亮灯
void WS2812B_Set_single_Light(uint8_t place, rgb_color COLOR, uint8_t Light)
{
    //不亮的灯(黑色)
    RGB_SetMore_Color(0, Pixel_NUM, BLACK_COLOR);
    WS2812B_Set_Color_Light(place, COLOR, Light);
    //亮灯
    WS2812B_Send_All();
}
//测试程序
void WS2812B_Set_TEST_light(uint32_t  delays)
{
    //百分比跑马
    for(uint8_t j = 0; j < BLACK_COLOR; j ++)
    {
        for(uint8_t i = 0; i <= 100; i ++)
        {
            WS2812B_Set_percent_Light(i, j, 1);
            delay_1ms(delays);
            fwdgt_counter_reload();//清除看门狗
        }
    }

    //单个灯跑马
    for(uint8_t j = 0; j < BLACK_COLOR; j ++)
    {
        for(uint8_t i = 0; i <= Pixel_NUM; i ++)
        {
            WS2812B_Set_single_Light(i, j, i);
            delay_1ms(delays);
            fwdgt_counter_reload();//清除看门狗
        }
    }

    //交替呼吸
    for(uint8_t j = 0; j < BLACK_COLOR; j ++)
    {
        for(uint8_t i = 0; i <= 100; i++)
        {
            WS2812B_Set_percent_Light(100, j, i);
            delay_1ms(delays);
            fwdgt_counter_reload();//清除看门狗
        }

        for(uint8_t i = 100; i > 0; i--)
        {
            WS2812B_Set_percent_Light(100, j, i);
            delay_1ms(delays * 2);
            fwdgt_counter_reload();//清除看门狗
        }
    }
}











### 关于GD32E230芯片使用DMA和IDLE进行串口接收的实现方法 #### 初始化配置 在GD32E230中,通过DMA配合IDLE中断可以高效地完成串口数据的接收任务。由于该系列芯片仅有一个DMA控制器,因此无需像其他型号(如GD32F103)那样指定具体的DMA控制器,只需选择合适的DMA通道即可[^3]。 以下是初始化过程的关键部分: - **头文件引入**:需要包含与串口、DMA以及GPIO相关的头文件。 - **串口初始化**:设置波特率、停止位、校验位等参数。 - **DMA初始化**:配置DMA传输方向、缓冲区大小、优先级等内容。 ```c #include "gd32e230.h" void usart_dma_init(void) { rcu_periph_clock_enable(RCU_GPIOA); rcu_periph_clock_enable(RCU_USART0); rcu_periph_clock_enable(RCU_DMA0); gpio_init(GPIOA, GPIO_MODE_AF_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_9); // TX gpio_init(GPIOA, GPIO_MODE_IN_FLOATING, GPIO_OSPEED_50MHZ, GPIO_PIN_10); // RX usart_deinit(USART0); usart_baudrate_set(USART0, 115200); usart_word_length_set(USART0, USART_WL_8BIT); usart_stop_bit_set(USART0, USART_STB_1BIT); usart_parity_config(USART0, USART_PM_NONE); usart_hardware_flow_rts_config(USART0, USART_RTS_DISABLE); usart_hardware_flow_cts_config(USART0, USART_CTS_DISABLE); usart_receive_config(USART0, USART_RECEIVE_ENABLE); usart_transmit_config(USART0, USART_TRANSMIT_ENABLE); } ``` --- #### 配置DMA用于串口接收 为了支持基于DMA的串口接收功能,需进一步配置DMA的相关属性: ```c void dma_usart_rx_init(void) { dma_parameter_struct dma_init_struct; dma_deinit(DMA0, DMA_CH4); dma_init_struct.direction = DMA_MEMORY_TO_PERIPHERAL; dma_init_struct.memory_width = DMA_MEMORY_WIDTH_8BIT; dma_init_struct.peripheral_width = DMA_PERIPHERAL_WIDTH_8BIT; dma_init_struct.priority = DMA_PRIORITY_HIGH; dma_init_struct.number = RECV_BUF_SIZE; // 接收缓冲区大小 dma_init_struct.memory_addr = (uint32_t)recv_buf; // 缓冲区指针 dma_init_struct.peripheral_addr = (uint32_t)&USART_DATA(USART0); dma_init(DMA0, DMA_CH4, &dma_init_struct); dma_circulation_disable(DMA0, DMA_CH4); dma_memory_to_memory_disable(DMA0, DMA_CH4); /* 启用DMA请求 */ usart_dma_receive_config(USART0, USART_DENR_DMAR_ENABLE); dma_channel_enable(DMA0, DMA_CH4); } ``` --- #### IDLE中断处理逻辑 当接收到一段完整的数据流并进入空闲状态时,会触发IDLE中断。此时可通过清空中断标志来结束当前接收周期,并重新准备下一轮接收操作。 ```c void usart0_idle_isr(void) { volatile uint32_t temp; if (RESET != usart_interrupt_flag_get(USART0, USART_INT_FLAG_IDLE)) { /* 清除IDLE中断标志 */ temp = usart_data_receive(USART0); // 必须读取一次数据寄存器以清除标志 usart_interrupt_flag_clear(USART0, USART_INT_FLAG_IDLE); /* 停止当前DMA传输 */ dma_channel_disable(DMA0, DMA_CH4); /* 处理已接收到的数据 */ process_received_data(recv_buf, recv_index); /* 准备新一轮接收 */ memset(recv_buf, 0, sizeof(recv_buf)); recv_index = 0; /* 重启DMA传输 */ dma_memory_address_config(DMA0, DMA_CH4, (uint32_t)recv_buf); dma_transfer_number_config(DMA0, DMA_CH4, RECV_BUF_SIZE); dma_channel_enable(DMA0, DMA_CH4); } } ``` 注意,在GD32E230中可以直接通过写入`IDLEC`位的方式清除IDLE中断标志,而需要额外的操作步骤[^3]。 --- #### 完整流程总结 以上代码展示了如何利用DMA和IDLE机制实现高效的串口数据接收。具体来说: - 利用DMA减少CPU干预频率; - 使用IDLE中断检测数据帧边界; - 结合两者可构建稳定可靠的异步通信方案。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值