1 氛围灯
氛围灯的应用场景有汽车装饰、智能家居、娱乐场所、办公和学习环境等,也可以结合DMX512协议做一些舞台灯光效果,在此总结一下esp32c2驱动ws2812的方法
2 测试环境
3 实现方法
idf只有RMT方式驱动灯条的例子,而esp32c2刚好不支持rmt,
这里总结一下gpio和spi方式来驱动ws2812,希望能帮到一些人
采用单线归零方式驱动
根据IO的电平持续时间来区分逻辑0和逻辑1
绿色数据先发,高位在前
3.1 gpio方式
3.1.1 实现纳秒延时
static volatile gpio_dev_t *get_dev = GPIO_HAL_GET_HW(GPIO_PORT_0); int8_t gpio_test_delay(led_strip_t *handle, uint8_t set_delay){ volatile uint8_t cnt = 0; portDISABLE_INTERRUPTS(); get_dev->out_w1ts.val = (1<<4UL); delay_test(set_delay); get_dev->out_w1tc.val = (1<<4UL); portENABLE_INTERRUPTS(); return 0;}
用逻辑分析仪测试高电平的时间
当cnt为1时
当cnt为2时
当cnt为6时
3.1.2 发送数据
这里取cnt等于2时作为330ns的延时,取cnt为6时作为660ns的延时
static void io_write_data(uint8_t data, uint8_t gpio_pin){ volatile uint8_t cnt = 2; for(uint8_t i=0;i<8;i++) { if(data & 0x80)//高位先发 { get_dev->out_w1ts.val = (1<delay_660ns(); get_dev->out_w1tc.val = (1< delay_660ns(); } else { get_dev->out_w1ts.val = (1< delay_330ns(); get_dev->out_w1tc.val = (1< delay_660ns(); } data<<=1; }}
3.1.3 设置颜色
static int8_t gpio_set_color(led_strip_t *handle, const uint32_t color){ uint8_t green = (uint8_t)(color>>8);//g uint8_t red = (uint8_t)(color>>16);//r uint8_t blue = (uint8_t)(color);//b uint8_t get_pin = handle->config.gpio_pin; io_write_data(0,get_pin); io_write_data(0,get_pin); io_write_data(0,get_pin); usleep(300); portDISABLE_INTERRUPTS(); for (uint8_t i=0; iled_cnt; i++) { io_write_data(green,get_pin); io_write_data(red,get_pin); io_write_data(blue,get_pin); } portENABLE_INTERRUPTS(); usleep(300); return 0;}
设置RGB颜色为FF00FF的实测波形
3.2 spi方式
3.2.1 写数据
设置spi的时钟为6MHz,那么一个时钟周期的时间为(1/6M)167ns,
当想表示逻辑1的时候,
需要高电平持续580ns~1us,然后是580ns~1us的低电平
需要4个时钟的高电平和4个时钟的低电平,都是167*4=668ns刚好满足条件,数据为0xF0
当想表示逻辑0的时候,
需要高电平持续220ns~380ns,然后是580ns~1us的低电平
需要2个时钟的高电平=2*167=334ns,6个时钟的低电平=6*167=1002ns=1us,数据为0xC0
static int8_t spi_send_data(spi_device_handle_t handle, const uint8_t *data, int len){ esp_err_t ret; spi_transaction_t t; if (len==0) return -1; //no need to send anything memset(&t, 0, sizeof(t)); //Zero out the transaction t.length=len*8; //Len is in bytes, transaction length is in bits. t.tx_buffer=data; //Data t.user=(void*)1; //D/C needs to be set to 1 ret=spi_device_polling_transmit(handle, &t); //Transmit! if (ret) { return -1; } return 0;} static int8_t spi_set_color(led_strip_t *handle, const uint32_t color){ uint8_t color_rgb_buf[3]; color_rgb_buf[0] = (uint8_t)(color>>8);//g color_rgb_buf[1] = (uint8_t)(color>>16);//r color_rgb_buf[2] = (uint8_t)(color);//b uint8_t data[24]; uint8_t get_high = handle->config.spi_config.spi_high_level_data; uint8_t get_low = handle->config.spi_config.spi_low_level_data; for(uint8_t j = 0 ; j<3; j++) { uint8_t cr = color_rgb_buf[j]; uint8_t mask = 0x80; for(uint8_t k = 0; k<8; k++){ data[j*8+k] = (mask & cr) ? get_high : get_low; mask>>=1; } } for (uint8_t i=0; iled_cnt; i++) { if (spi_send_data(handle->config.spi_config.handle,data,24)) { return -1; } } return 0;}
3.2.2 实测波形
逻辑0:350ns高电平,1.05us低电平,
逻辑1:高电平和低电平都为700ns 设置RGB颜色为FF00FF的实测波形
led_strip_t led_strip; void app_main(void){ spi_config_t spi_config = { .mosi_pin=4, .spi_high_level_data=0xf0, .spi_low_level_data=0xc0, .clock_speed_hz = 6*1000*1000, .host_id=SPI2_HOST, }; memcpy(&led_strip.config.spi_config,&spi_config,sizeof(spi_config_t)); uint8_t io_type=LED_STRIP_CTL_SPI; led_strip.config.gpio_pin = 4; led_strip_init(&led_strip,io_type,8); led_strip_config_flash_ivt_ms(&led_strip,1000); led_strip_update_rgb(&led_strip,0xffffff); led_strip_set_state(&led_strip,LED_STRIP_STATE_BREATH); for (;;) { led_strip_handle(&led_strip); vTaskDelay(pdMS_TO_TICKS(10)); } }
5 实验效果
6 总结
esp32的api占用时间过长,使用gpio方式驱动ws2812的话,无法像stm32那样可以轻松实现纳秒级别延时,需要结合逻辑分析仪来做实际测试,所以选择spi方式会更好一些。