add neopixel led strip driver for saola, make saola as an component

This commit is contained in:
hathach 2020-04-09 00:06:18 +07:00
parent bfde988af3
commit cefbd9579c
6 changed files with 340 additions and 14 deletions

View File

@ -2,13 +2,14 @@
# CMakeLists in this exact order for cmake to work correctly
cmake_minimum_required(VERSION 3.5)
# example src directory
set(EXTRA_COMPONENT_DIRS "src")
# TOP is absolute path to root directory of TinyUSB git repo
set(TOP "../../..")
get_filename_component(TOP "${TOP}" REALPATH)
# Add example src and bsp directories
set(EXTRA_COMPONENT_DIRS "src" "${TOP}/hw/bsp/esp32s2_saola_1")
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
set(SUPPORTED_TARGETS esp32s2)
project(board_test)

View File

@ -13,7 +13,3 @@ target_include_directories(${COMPONENT_TARGET} PUBLIC
"${TOP}/hw"
"${TOP}/src"
)
target_sources(${COMPONENT_TARGET} PUBLIC
"${TOP}/hw/bsp/esp32s2_saola_1/esp32s2_saola_1.c"
)

View File

@ -0,0 +1,16 @@
idf_component_register(SRCS "esp32s2_saola_1.c" "led_strip/src/led_strip_rmt_ws2812.c"
INCLUDE_DIRS "led_strip/include"
PRIV_REQUIRES "driver"
REQUIRES freertos src)
target_compile_options(${COMPONENT_TARGET} PUBLIC
"-DCFG_TUSB_MCU=OPT_MCU_ESP32S2"
"-DCFG_TUSB_OS=OPT_OS_FREERTOS"
)
idf_component_get_property( FREERTOS_ORIG_INCLUDE_PATH freertos ORIG_INCLUDE_PATH)
target_include_directories(${COMPONENT_TARGET} PUBLIC
"${FREERTOS_ORIG_INCLUDE_PATH}"
"${TOP}/hw"
"${TOP}/src"
)

View File

@ -24,26 +24,41 @@
* This file is part of the TinyUSB stack.
*/
#include "bsp/board.h"
#include "../board.h"
#include "driver/gpio.h"
#include "hal/usb_hal.h"
#include "driver/rmt.h"
#include "led_strip/include/led_strip.h"
//--------------------------------------------------------------------+
// MACRO TYPEDEF CONSTANT ENUM DECLARATION
//--------------------------------------------------------------------+
// TODO use saola-1 on-board neopixel (WS2812)
#define LED_PIN 21
// Note: On the production version (v1.2) WS2812 is connected to GPIO 18,
// however earlier revision v1.1 WS2812 is connected to GPIO 17
//#define LED_PIN 18 // v1.2 and later
#define LED_PIN 17 // v1.1
#define BUTTON_PIN 0
#define BUTTON_STATE_ACTIVE 0
static led_strip_t *strip;
// Initialize on-board peripherals : led, button, uart and USB
void board_init(void)
{
// LED
gpio_pad_select_gpio(LED_PIN);
gpio_set_direction(LED_PIN, GPIO_MODE_OUTPUT);
// WS2812 Neopixel driver with RMT peripheral
rmt_config_t config = RMT_DEFAULT_CONFIG_TX(LED_PIN, RMT_CHANNEL_0);
config.clk_div = 2; // set counter clock to 40MHz
rmt_config(&config);
rmt_driver_install(config.channel, 0, 0);
led_strip_config_t strip_config = LED_STRIP_DEFAULT_CONFIG(1, (led_strip_dev_t) config.channel);
strip = led_strip_new_rmt_ws2812(&strip_config);
strip->clear(strip, 100); // off led
// Button
gpio_pad_select_gpio(BUTTON_PIN);
@ -60,7 +75,8 @@ void board_init(void)
// Turn LED on or off
void board_led_write(bool state)
{
gpio_set_level(LED_PIN, state);
strip->set_pixel(strip, 0, (state ? 0x88 : 0x00), 0x00, 0x00);
strip->refresh(strip, 100);
}
// Get the current state of button

View File

@ -0,0 +1,126 @@
// Copyright 2019 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
#include "esp_err.h"
/**
* @brief LED Strip Type
*
*/
typedef struct led_strip_s led_strip_t;
/**
* @brief LED Strip Device Type
*
*/
typedef void *led_strip_dev_t;
/**
* @brief Declare of LED Strip Type
*
*/
struct led_strip_s {
/**
* @brief Set RGB for a specific pixel
*
* @param strip: LED strip
* @param index: index of pixel to set
* @param red: red part of color
* @param green: green part of color
* @param blue: blue part of color
*
* @return
* - ESP_OK: Set RGB for a specific pixel successfully
* - ESP_ERR_INVALID_ARG: Set RGB for a specific pixel failed because of invalid parameters
* - ESP_FAIL: Set RGB for a specific pixel failed because other error occurred
*/
esp_err_t (*set_pixel)(led_strip_t *strip, uint32_t index, uint32_t red, uint32_t green, uint32_t blue);
/**
* @brief Refresh memory colors to LEDs
*
* @param strip: LED strip
* @param timeout_ms: timeout value for refreshing task
*
* @return
* - ESP_OK: Refresh successfully
* - ESP_ERR_TIMEOUT: Refresh failed because of timeout
* - ESP_FAIL: Refresh failed because some other error occurred
*
* @note:
* After updating the LED colors in the memory, a following invocation of this API is needed to flush colors to strip.
*/
esp_err_t (*refresh)(led_strip_t *strip, uint32_t timeout_ms);
/**
* @brief Clear LED strip (turn off all LEDs)
*
* @param strip: LED strip
* @param timeout_ms: timeout value for clearing task
*
* @return
* - ESP_OK: Clear LEDs successfully
* - ESP_ERR_TIMEOUT: Clear LEDs failed because of timeout
* - ESP_FAIL: Clear LEDs failed because some other error occurred
*/
esp_err_t (*clear)(led_strip_t *strip, uint32_t timeout_ms);
/**
* @brief Free LED strip resources
*
* @param strip: LED strip
*
* @return
* - ESP_OK: Free resources successfully
* - ESP_FAIL: Free resources failed because error occurred
*/
esp_err_t (*del)(led_strip_t *strip);
};
/**
* @brief LED Strip Configuration Type
*
*/
typedef struct {
uint32_t max_leds; /*!< Maximum LEDs in a single strip */
led_strip_dev_t dev; /*!< LED strip device (e.g. RMT channel, PWM channel, etc) */
} led_strip_config_t;
/**
* @brief Default configuration for LED strip
*
*/
#define LED_STRIP_DEFAULT_CONFIG(number, dev_hdl) \
{ \
.max_leds = number, \
.dev = dev_hdl, \
}
/**
* @brief Install a new ws2812 driver (based on RMT peripheral)
*
* @param config: LED strip configuration
* @return
* LED strip instance or NULL
*/
led_strip_t *led_strip_new_rmt_ws2812(const led_strip_config_t *config);
#ifdef __cplusplus
}
#endif

View File

@ -0,0 +1,171 @@
// Copyright 2019 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include <stdlib.h>
#include <string.h>
#include <sys/cdefs.h>
#include "esp_log.h"
#include "esp_attr.h"
#include "led_strip.h"
#include "driver/rmt.h"
static const char *TAG = "ws2812";
#define STRIP_CHECK(a, str, goto_tag, ret_value, ...) \
do \
{ \
if (!(a)) \
{ \
ESP_LOGE(TAG, "%s(%d): " str, __FUNCTION__, __LINE__, ##__VA_ARGS__); \
ret = ret_value; \
goto goto_tag; \
} \
} while (0)
#define WS2812_T0H_NS (350)
#define WS2812_T0L_NS (1000)
#define WS2812_T1H_NS (1000)
#define WS2812_T1L_NS (350)
#define WS2812_RESET_US (280)
static uint32_t ws2812_t0h_ticks = 0;
static uint32_t ws2812_t1h_ticks = 0;
static uint32_t ws2812_t0l_ticks = 0;
static uint32_t ws2812_t1l_ticks = 0;
typedef struct {
led_strip_t parent;
rmt_channel_t rmt_channel;
uint32_t strip_len;
uint8_t buffer[0];
} ws2812_t;
/**
* @brief Conver RGB data to RMT format.
*
* @note For WS2812, R,G,B each contains 256 different choices (i.e. uint8_t)
*
* @param[in] src: source data, to converted to RMT format
* @param[in] dest: place where to store the convert result
* @param[in] src_size: size of source data
* @param[in] wanted_num: number of RMT items that want to get
* @param[out] translated_size: number of source data that got converted
* @param[out] item_num: number of RMT items which are converted from source data
*/
static void IRAM_ATTR ws2812_rmt_adapter(const void *src, rmt_item32_t *dest, size_t src_size,
size_t wanted_num, size_t *translated_size, size_t *item_num)
{
if (src == NULL || dest == NULL) {
*translated_size = 0;
*item_num = 0;
return;
}
const rmt_item32_t bit0 = {{{ ws2812_t0h_ticks, 1, ws2812_t0l_ticks, 0 }}}; //Logical 0
const rmt_item32_t bit1 = {{{ ws2812_t1h_ticks, 1, ws2812_t1l_ticks, 0 }}}; //Logical 1
size_t size = 0;
size_t num = 0;
uint8_t *psrc = (uint8_t *)src;
rmt_item32_t *pdest = dest;
while (size < src_size && num < wanted_num) {
for (int i = 0; i < 8; i++) {
// MSB first
if (*psrc & (1 << (7 - i))) {
pdest->val = bit1.val;
} else {
pdest->val = bit0.val;
}
num++;
pdest++;
}
size++;
psrc++;
}
*translated_size = size;
*item_num = num;
}
static esp_err_t ws2812_set_pixel(led_strip_t *strip, uint32_t index, uint32_t red, uint32_t green, uint32_t blue)
{
esp_err_t ret = ESP_OK;
ws2812_t *ws2812 = __containerof(strip, ws2812_t, parent);
STRIP_CHECK(index < ws2812->strip_len, "index out of the maximum number of leds", err, ESP_ERR_INVALID_ARG);
uint32_t start = index * 3;
// In thr order of GRB
ws2812->buffer[start + 0] = green & 0xFF;
ws2812->buffer[start + 1] = red & 0xFF;
ws2812->buffer[start + 2] = blue & 0xFF;
return ESP_OK;
err:
return ret;
}
static esp_err_t ws2812_refresh(led_strip_t *strip, uint32_t timeout_ms)
{
esp_err_t ret = ESP_OK;
ws2812_t *ws2812 = __containerof(strip, ws2812_t, parent);
STRIP_CHECK(rmt_write_sample(ws2812->rmt_channel, ws2812->buffer, ws2812->strip_len * 3, true) == ESP_OK,
"transmit RMT samples failed", err, ESP_FAIL);
return rmt_wait_tx_done(ws2812->rmt_channel, pdMS_TO_TICKS(timeout_ms));
err:
return ret;
}
static esp_err_t ws2812_clear(led_strip_t *strip, uint32_t timeout_ms)
{
ws2812_t *ws2812 = __containerof(strip, ws2812_t, parent);
// Write zero to turn off all leds
memset(ws2812->buffer, 0, ws2812->strip_len * 3);
return ws2812_refresh(strip, timeout_ms);
}
static esp_err_t ws2812_del(led_strip_t *strip)
{
ws2812_t *ws2812 = __containerof(strip, ws2812_t, parent);
free(ws2812);
return ESP_OK;
}
led_strip_t *led_strip_new_rmt_ws2812(const led_strip_config_t *config)
{
led_strip_t *ret = NULL;
STRIP_CHECK(config, "configuration can't be null", err, NULL);
// 24 bits per led
uint32_t ws2812_size = sizeof(ws2812_t) + config->max_leds * 3;
ws2812_t *ws2812 = calloc(1, ws2812_size);
STRIP_CHECK(ws2812, "request memory for ws2812 failed", err, NULL);
uint32_t counter_clk_hz = 0;
STRIP_CHECK(rmt_get_counter_clock((rmt_channel_t)config->dev, &counter_clk_hz) == ESP_OK,
"get rmt counter clock failed", err, NULL);
// ns -> ticks
float ratio = (float)counter_clk_hz / 1e9;
ws2812_t0h_ticks = (uint32_t)(ratio * WS2812_T0H_NS);
ws2812_t0l_ticks = (uint32_t)(ratio * WS2812_T0L_NS);
ws2812_t1h_ticks = (uint32_t)(ratio * WS2812_T1H_NS);
ws2812_t1l_ticks = (uint32_t)(ratio * WS2812_T1L_NS);
// set ws2812 to rmt adapter
rmt_translator_init((rmt_channel_t)config->dev, ws2812_rmt_adapter);
ws2812->rmt_channel = (rmt_channel_t)config->dev;
ws2812->strip_len = config->max_leds;
ws2812->parent.set_pixel = ws2812_set_pixel;
ws2812->parent.refresh = ws2812_refresh;
ws2812->parent.clear = ws2812_clear;
ws2812->parent.del = ws2812_del;
return &ws2812->parent;
err:
return ret;
}