TC321X驱动ws2812历程
前言本文采用的主控芯片是TC321X比较小众前面做了很多失败的尝试正确驱动直接跳转到——3.通过PWMDMA驱动ws2812ws2812使用单总线通信对时序要求很高要达到ns级别所以最好的方法是PWM驱动串口模拟发送也可以不过会有大量延时1.WS2812通信协议ws2812通信为单总线通信灯光时序为grb时序通过发送24bit数据G7 G6 G5 G4 G3 G2 G1 G0 R7 R6 R5 R4 R3 R2 R1 R0 B7 B6 B5 B4 B3 B2 B1 B0如发送1 1 1 1 1 1 1 10 0 0 0 0 0 0 00 0 0 0 0 0 0 0即向ws2812发送一个亮绿灯的多个ws2812连接时发送n个灯光数据即发送n个24bit数据后再发送RES数据。根据ws2812b手册ws2812b的0码1码的高低电平持续时间T0H0 码 高电平时间220ns~380nsT1H1 码 高电平时间580ns~1µsT0L0 码 低电平时间580ns~1µsT1L1 码 低电平时间580ns~1µsRES帧单位低电平时间280µs 以上T0HT0L、T1HT1L≥1.25µs2.通过PWMDMA占空比100%驱动ws2812一种之前比较常用的方法控制ws2812b为配置PWM的占空比为100%通过控制脉冲数pulse_num来控制高低电平的持续时间从而发送1码和0码。#define WS2812_BIT_FREQ 8000000 //8m Hz //周期: 1.25us 48MHz 60 ticks #define WS2812_PERIOD_TICK (CLOCK_SYS_CLOCK_HZ / WS2812_BIT_FREQ) pwm_set_clk(CLOCK_SYS_CLOCK_HZ, CLOCK_SYS_CLOCK_HZ); gpio_set_func(PWM_PIN, AS_PWMx); pwm_set_mode(PWM_ID, PWM_IR_DMA_FIFO_MODE); //NORMAL:100%占空比 pwm_set_cycle_and_duty(PWM0_ID, WS2812_PERIOD_TICK, WS2812_PERIOD_TICK); //SHADOW:100%占空比 pwm_set_pwm0_shadow_cycle_and_duty(WS2812_PERIOD_TICK, WS2812_PERIOD_TICK); unsigned char index2; uint32_t grb 0xFFFF00; // 黄色: G0xFF R0xFF B0x00 GRB 0xFFFF00 // 10个LED每bit用1条waveform配置 for (int led 0; led 10; led) { for (int bit 23; bit 0; bit--) { if (grb BIT(bit)) { // 1码: NORMAL(60%占空比), 1个周期 高750ns 低500ns IR_DMA_Buff[index] pwm_config_dma_fifo_waveform(1, PWM0_PULSE_NORMAL, 6); // 1码高750ns IR_DMA_Buff[index] pwm_config_dma_fifo_waveform(0, PWM0_PULSE_NORMAL, 4); // 1码低500ns } else { IR_DMA_Buff[index] pwm_config_dma_fifo_waveform(1, PWM0_PULSE_NORMAL, 3); // 0码高375ns IR_DMA_Buff[index] pwm_config_dma_fifo_waveform(0, PWM0_PULSE_NORMAL, 7); // 0码低875ns } } } // 尾部Reset锁存颜色(280us低电平) 280us / 1.25us 224个周期 IR_DMA_Buff[index] pwm_config_dma_fifo_waveform(0, PWM0_PULSE_NORMAL, 224); unsigned int length index*2 - 4;//The first four bytes are data length bytes, not included in the actual length to be sent unsigned char* buff (unsigned char*)IR_DMA_Buff[0]; buff[0] length0xff; buff[1] (length8)0xff; buff[2] (length16)0xff; buff[3] (length24)0xff; pwm_set_dma_address(IR_DMA_Buff); pwm_set_interrupt_enable(PWM_IRQ_PWM0_IR_DMA_FIFO_DONE);但经过测试后发现只能发出5个灯的载波后面5个灯的发送不了原因是buffer太大大小为(120*21)*2482 bit数据120*2是一个周期发了两个载波后面尝试DMA_pingpong模式发现也有问题1.占空比100%周期实际为0.125us周期太短无法切换到下一个chain#elif(DMA_MODE DMA_PINGPONG_MODE) //Initialize chain 0 waveform data unsigned short *ptr0 dma_chain[0].buf; unsigned short *start_ptr0 ptr0; uint32_t grb 0xFFFF00; // 10个LED全亮绿色G0xFF R0x00 B0x00 编码为0x00FF00 for (int led 0; led 5; led) { // 高位在先G (8bit) → R (8bit) → B (8bit) for (int bit 23; bit 0; bit--) { if (grb BIT(bit)) { *ptr0 pwm_config_dma_fifo_waveform(1, PWM0_PULSE_NORMAL, 6); // 1码高750ns *ptr0 pwm_config_dma_fifo_waveform(0, PWM0_PULSE_NORMAL, 4); // 1码低500ns } else { *ptr0 pwm_config_dma_fifo_waveform(1, PWM0_PULSE_NORMAL, 3); // 0码高375ns *ptr0 pwm_config_dma_fifo_waveform(0, PWM0_PULSE_NORMAL, 7); // 0码低875ns } } } //you can continue to add fifo_waveform, dma_chain[0] can be configured with a maximum of 255 groups, i.e., 510byte dma_chain[0].cfg_num ptr0 - start_ptr0; dma_chain[0].size dma_chain[0].cfg_num * sizeof(short); //Set the Chain 0 DMA Length(bytes) IR_DMA_Buff[0] dma_chain[0].size 0xFFFF; IR_DMA_Buff[1] (dma_chain[0].size 16) 0xFFFF; printf(dma_chain[0].cfg_num %d\n, dma_chain[0].cfg_num); //Initialize chain 1 waveform data unsigned short *ptr1 dma_chain[1].buf; unsigned short *start_ptr1 ptr1; // 10个LED全亮绿色G0xFF R0x00 B0x00 编码为0x00FF00 for (int led 0; led 5; led) { // 高位在先G (8bit) → R (8bit) → B (8bit) for (int bit 23; bit 0; bit--) { if (grb BIT(bit)) { *ptr1 pwm_config_dma_fifo_waveform(1, PWM0_PULSE_NORMAL, 6); // 1码高750ns *ptr1 pwm_config_dma_fifo_waveform(0, PWM0_PULSE_NORMAL, 4); // 1码低500ns } else { *ptr1 pwm_config_dma_fifo_waveform(1, PWM0_PULSE_NORMAL, 3); // 0码高375ns *ptr1 pwm_config_dma_fifo_waveform(0, PWM0_PULSE_NORMAL, 7); // 0码低875ns } } } // 尾部Reset锁存颜色 *ptr1 pwm_config_dma_fifo_waveform(0, PWM0_PULSE_NORMAL, 480); //you can continue to add fifo_waveform, dma_chain[1] can be configured with a maximum of 255 groups, i.e., 510byte dma_chain[1].cfg_num ptr1 - start_ptr1; dma_chain[1].size dma_chain[1].cfg_num * sizeof(short); //Set the Chain 1 DMA Length(bytes) IR_DMA_Buff[264] dma_chain[1].size 0xFFFF; IR_DMA_Buff[265] (dma_chain[1].size 16) 0xFFFF; printf(dma_chain[1].cfg_num %d\n, dma_chain[1].cfg_num); pwm_set_dma_address_pingpong(IR_DMA_Buff); pwm_set_interrupt_enable(PWM_IRQ_PWM0_PNUM); #endif周期长一点就可以所以后面才突然想起来改为正常PWMDMA驱动3.通过PWMDMA驱动ws2812使用PWMDMA驱动并使用正常PWM周期和shadow PWM周期来配置ws2812驱动的1码和0码1码: NORMAL(60%占空比), 1个周期 高750ns 低500ns0码: SHADOW(40%占空比), 1个周期 高350ns 低900ns#define PWM_ID PWM0_ID #define WS2812_BIT_FREQ 800000 //800K Hz ~1.25us per bit #define WS2812_T1H_NS 750 //1码高电平750ns #define WS2812_T0H_NS 350 //0码高电平350ns //周期: 1.25us 48MHz 60 ticks #define WS2812_PERIOD_TICK (CLOCK_SYS_CLOCK_HZ / WS2812_BIT_FREQ) //1码占空比: 750ns/1250ns 60% (NORMAL寄存器) #define WS2812_T1_CMP_TICK (WS2812_PERIOD_TICK * WS2812_T1H_NS / (1000000000 / WS2812_BIT_FREQ)) //0码占空比: 350ns/1250ns 40% (SHADOW寄存器) #define WS2812_T0_CMP_TICK (WS2812_PERIOD_TICK * WS2812_T0H_NS / (1000000000 / WS2812_BIT_FREQ)) void user_init(void) { gpio_set_func(PWM_PIN, AS_PWMx); pwm_set_mode(PWM_ID, PWM_IR_DMA_FIFO_MODE); //NORMAL: 60周期/36cmp 60%占空比 (1码: 高750ns低500ns) pwm_set_cycle_and_duty(PWM0_ID, WS2812_PERIOD_TICK, WS2812_T1_CMP_TICK); //SHADOW: 60周期/24cmp 40%占空比 (0码: 高350ns低750ns) pwm_set_pwm0_shadow_cycle_and_duty(WS2812_PERIOD_TICK, WS2812_T0_CMP_TICK); pwm_n_revert(PWM_ID); #if(DMA_MODE DMA_NORMAL_MODE) unsigned char index2; uint32_t grb 0xFFFF00; // 黄色: G0xFF R0xFF B0x00 GRB 0xFFFF00 // 10个LED每bit用1条waveform配置 for (int led 0; led 10; led) { for (int bit 23; bit 0; bit--) { if (grb BIT(bit)) { // 1码: NORMAL(60%占空比), 1个周期 高750ns 低500ns IR_DMA_Buff[index] pwm_config_dma_fifo_waveform(1, PWM0_PULSE_NORMAL, 1); } else { // 0码: SHADOW(40%占空比), 1个周期 高350ns 低900ns IR_DMA_Buff[index] pwm_config_dma_fifo_waveform(1, PWM0_PULSE_SHADOW, 1); } } } // 尾部Reset锁存颜色(280us低电平) 280us / 1.25us 224个周期 IR_DMA_Buff[index] pwm_config_dma_fifo_waveform(0, PWM0_PULSE_NORMAL, 224); unsigned int length index*2 - 4;//The first four bytes are data length bytes, not included in the actual length to be sent unsigned char* buff (unsigned char*)IR_DMA_Buff[0]; buff[0] length0xff; buff[1] (length8)0xff; buff[2] (length16)0xff; buff[3] (length24)0xff; pwm_set_dma_address(IR_DMA_Buff); pwm_set_interrupt_enable(PWM_IRQ_PWM0_IR_DMA_FIFO_DONE); }最终利用双PWM周期组成功解决PWMDMA驱动ws2812。PS关于使用DMA_PingPong Mode外加PWM的双波形组驱动ws2812的话两个波形间会有几us的间隔这里使用的是两个chain分别发5个灯的方式