Skip to content

Commit

Permalink
WIP "i2s_parallel" component based on espressif/esp-idf#5238
Browse files Browse the repository at this point in the history
WS   is working
DATA is not working
  • Loading branch information
Carlos Sobrinho committed May 20, 2020
1 parent 1dcfe79 commit 1536e1e
Show file tree
Hide file tree
Showing 8 changed files with 321 additions and 1 deletion.
4 changes: 4 additions & 0 deletions components/i2s_parallel/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
set(COMPONENT_SRCS "i2s_parallel.c")
set(COMPONENT_ADD_INCLUDEDIRS "include")

register_component()
Empty file.
194 changes: 194 additions & 0 deletions components/i2s_parallel/i2s_parallel.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,194 @@
#include <stdio.h>
#include <string.h>

#include "freertos/FreeRTOS.h"
#include "freertos/task.h"

#include "driver/gpio.h"

#include "esp_attr.h"
#include "esp_system.h"
#include "soc/apb_ctrl_reg.h"
#include "soc/dport_access.h"
#include "soc/dport_reg.h"
#include "soc/i2s_struct.h"
#include "soc/lldesc.h"

#include "i2s_parallel.h"

static i2s_parallel_pin_config_t i2s_parallel_bus;

#define DMA_MAX 64
static lldesc_t __dma[DMA_MAX] = {0};
static SemaphoreHandle_t tx_sem = NULL;
static uint8_t *i2s_parallel_buffer = NULL;

static void i2s_parallel_config(uint32_t clk_div)
{
DPORT_CLEAR_PERI_REG_MASK(DPORT_PERIP_CLK_EN_REG, DPORT_I2S0_CLK_EN);
DPORT_SET_PERI_REG_MASK(DPORT_PERIP_CLK_EN_REG, DPORT_I2S0_CLK_EN);
DPORT_SET_PERI_REG_MASK(DPORT_PERIP_RST_EN_REG, DPORT_I2S0_RST);
DPORT_CLEAR_PERI_REG_MASK(DPORT_PERIP_RST_EN_REG, DPORT_I2S0_RST);

//Configure pclk, max: 20M
I2S0.clkm_conf.val = 0;
I2S0.clkm_conf.clkm_div_num = 2;
I2S0.clkm_conf.clkm_div_b = 0;
I2S0.clkm_conf.clkm_div_a =63;
I2S0.clkm_conf.clk_en = 1;
I2S0.clkm_conf.clk_sel = 2;
I2S0.sample_rate_conf.val = 0;
I2S0.sample_rate_conf.tx_bck_div_num = clk_div;

I2S0.int_ena.val = 0;
I2S0.int_clr.val = ~0;

I2S0.conf.val = 0;
I2S0.conf.tx_right_first = 1;
I2S0.conf.tx_msb_right = 1;
I2S0.conf.tx_dma_equal = 1;

I2S0.conf1.val = 0;
I2S0.conf1.tx_pcm_bypass = 1;
I2S0.conf1.tx_stop_en = 1;
I2S0.timing.val = 0;
//Set lcd mode
I2S0.conf2.val = 0;
I2S0.conf2.lcd_en = 1;

I2S0.fifo_conf.val = 0;
I2S0.fifo_conf.tx_fifo_mod_force_en = 1;
I2S0.fifo_conf.tx_data_num = 32;
I2S0.fifo_conf.tx_fifo_mod = 4;

I2S0.conf_chan.tx_chan_mod = 0;//remove

I2S0.sample_rate_conf.tx_bits_mod = i2s_parallel_bus.bit_width;
printf("--------I2S version %x, bit_width: %d\n", I2S0.date, i2s_parallel_bus.bit_width);
}

static void i2s_parallel_set_pin(void)
{
PIN_FUNC_SELECT(GPIO_PIN_MUX_REG[i2s_parallel_bus.pin_clk], PIN_FUNC_GPIO);
gpio_set_direction(i2s_parallel_bus.pin_clk, GPIO_MODE_OUTPUT);
gpio_set_pull_mode(i2s_parallel_bus.pin_clk,GPIO_PULLUP_ONLY);
gpio_matrix_out(i2s_parallel_bus.pin_clk, I2S0O_WS_OUT_IDX, true, 0);

for(int i = 0; i < i2s_parallel_bus.bit_width; i++) {
PIN_FUNC_SELECT(GPIO_PIN_MUX_REG[i2s_parallel_bus.data[i]], PIN_FUNC_GPIO);
gpio_set_direction(i2s_parallel_bus.data[i], GPIO_MODE_OUTPUT);
gpio_set_pull_mode(i2s_parallel_bus.data[i],GPIO_PULLUP_ONLY);
gpio_matrix_out(i2s_parallel_bus.data[i], I2S0O_DATA_OUT0_IDX + i, false, 0);
}
}

static void IRAM_ATTR _i2s_isr(void *arg)
{
if(I2S0.int_st.out_total_eof) {
BaseType_t HPTaskAwoken = pdFALSE;
xSemaphoreGiveFromISR(tx_sem, &HPTaskAwoken);
if(HPTaskAwoken == pdTRUE) {
portYIELD_FROM_ISR();
}
}
I2S0.int_clr.val = ~0;
}

static void i2s_parallel_interface_init(uint32_t clk_div)
{
for(int i = 0; i < DMA_MAX; i++) {
__dma[i].size = 0;
__dma[i].length = 0;
__dma[i].sosf = 0;
__dma[i].eof = 1;
__dma[i].owner = 1;
__dma[i].buf = NULL;
__dma[i].empty = 0;
}
i2s_parallel_set_pin();
i2s_parallel_config(clk_div);
tx_sem = xSemaphoreCreateBinary();
esp_intr_alloc(ETS_I2S0_INTR_SOURCE, 0, _i2s_isr, NULL, NULL);
I2S0.int_ena.out_total_eof = 1;
}

static inline void i2s_dma_start(void)
{
I2S0.fifo_conf.dscr_en = 1;
I2S0.out_link.start = 1;
I2S0.conf.tx_start = 1;
xSemaphoreTake(tx_sem, portMAX_DELAY);
while (!I2S0.state.tx_idle);
I2S0.conf.tx_start = 0;
I2S0.fifo_conf.dscr_en = 0;
I2S0.conf.tx_reset = 1;
I2S0.conf.tx_reset = 0;
}

static inline void i2s_parallel_dma_write(const uint8_t *buf, size_t length)
{
int len = length;
int trans_len = 0;
int cnt = 0;
while(len) {
trans_len = len > 4000 ? 4000 : len;
__dma[cnt].size = trans_len;
__dma[cnt].length = trans_len;
__dma[cnt].buf = buf;
__dma[cnt].eof = 0;
__dma[cnt].empty = (uint32_t) &__dma[cnt+1];
buf += trans_len;
len -= trans_len;
cnt++;
}
__dma[cnt-1].eof = 1;
__dma[cnt-1].empty = 0;
I2S0.out_link.addr = ((uint32_t)&__dma[0]) & 0xfffff;
i2s_dma_start();
}

void i2s_parallel_write_data(uint8_t *data, size_t len)
{
#ifdef I2S_PARALLEL_BURST_BUFFER_SIZE
for (int x = 0; x < len / I2S_PARALLEL_BURST_BUFFER_SIZE; x++) {
memcpy(i2s_parallel_buffer, data, I2S_PARALLEL_BURST_BUFFER_SIZE);
i2s_parallel_dma_write(i2s_parallel_buffer, I2S_PARALLEL_BURST_BUFFER_SIZE);
data += I2S_PARALLEL_BURST_BUFFER_SIZE;
}
if (len % I2S_PARALLEL_BURST_BUFFER_SIZE) {
memcpy(i2s_parallel_buffer, data, len % I2S_PARALLEL_BURST_BUFFER_SIZE);
i2s_parallel_dma_write(i2s_parallel_buffer, len % I2S_PARALLEL_BURST_BUFFER_SIZE);
}
#else
i2s_parallel_dma_write((uint8_t *)data, len);
#endif
}

void i2s_parallel_init(i2s_parallel_pin_config_t *pin_config, uint32_t clk_div)
{
if (pin_config == NULL) {
return;
}
i2s_parallel_bus = *pin_config;
#ifdef I2S_PARALLEL_BURST_BUFFER_SIZE
i2s_parallel_buffer = (uint8_t *)heap_caps_malloc(I2S_PARALLEL_BURST_BUFFER_SIZE, MALLOC_CAP_DMA);
#endif
i2s_parallel_interface_init(clk_div);
}

void i2s_parallel_write_data16n(uint16_t data, size_t len)
{
uint16_t *tmp = (uint16_t *) i2s_parallel_buffer;
bool filled = false;
while (len > 0) {
size_t to_write = len >= I2S_PARALLEL_BURST_BUFFER_SIZE ? I2S_PARALLEL_BURST_BUFFER_SIZE : len;
if (!filled) {
for (int i = 0; i < to_write; i += sizeof(uint16_t)) {
*tmp++ = data;
}
filled = true;
}
i2s_parallel_dma_write(i2s_parallel_buffer, to_write);
len -= to_write;
}
}
28 changes: 28 additions & 0 deletions components/i2s_parallel/include/i2s_parallel.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
#pragma once

#include <stdio.h>
#include <stdint.h>
#include "driver/i2s.h"

#ifdef __cplusplus
extern "C" {
#endif

#define I2S_PARALLEL_BURST_BUFFER_SIZE (16 * 1024)

typedef struct {
uint8_t bit_width;
uint8_t pin_clk;
uint8_t data[24];
} i2s_parallel_pin_config_t;

void i2s_parallel_write_data(uint8_t *data, size_t len);

void i2s_parallel_write_data16n(uint16_t data, size_t len);

void i2s_parallel_init(i2s_parallel_pin_config_t *pin_config, uint32_t clk_div); // clk_fre = 40MHz / clk_div, clk_div > 1

#ifdef __cplusplus
}
#endif

2 changes: 1 addition & 1 deletion main/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,6 @@ idf_component_register(
REQUIRES
# Internal components.
"protos" "bme280" "esp32-ds18b20" "esp32-owb" "esp32-rotary-encoder" "esp-google-iot" "u8g2" "ucglib"
"esp-ccronexpr" "lcd_common"
"esp-ccronexpr" "lcd_common" "i2s_parallel"
# External components.
"console" "json" "nvs_flash")
69 changes: 69 additions & 0 deletions main/driver/lcd/i2s_p.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
#include "driver/gpio.h"

#include "esp_err.h"

#include "i2s_parallel.h"

#include "error.h"
#include "i2s_p.h"
#include "lcd.h"

static const char *TAG = "lcd_i2s_p";

esp_err_t lcd_i2s_parallel_init(lcd_dev_t *dev) {
ARG_CHECK(dev != NULL, ERR_PARAM_NULL);
ARG_CHECK(dev->config.data_width == 8, "data_width must be 8");
ARG_CHECK(dev->config.ws_io_num != GPIO_NUM_NC, "ws_io_num is GPIO_NUM_NC");
ARG_CHECK(dev->config.rs_io_num != GPIO_NUM_NC, "rs_io_num is GPIO_NUM_NC");

dev->handle = NULL;

// Setup the GPIOs as general purpose outputs.
uint64_t mask = 0;
if (dev->config.rst_io_num != GPIO_NUM_NC) {
gpio_pad_select_gpio(dev->config.rst_io_num);
mask |= BIT64(dev->config.rst_io_num);
}
if (dev->config.rd_io_num != GPIO_NUM_NC) {
gpio_pad_select_gpio(dev->config.rd_io_num);
mask |= BIT64(dev->config.rd_io_num);
}
gpio_pad_select_gpio(dev->config.rs_io_num);
mask |= BIT64(dev->config.rs_io_num);
const gpio_config_t conf = {
.pin_bit_mask = mask,
.mode = GPIO_MODE_OUTPUT,
.pull_up_en = GPIO_PULLUP_DISABLE,
.pull_down_en = GPIO_PULLDOWN_DISABLE,
.intr_type = GPIO_INTR_DISABLE,
};
ESP_ERROR_CHECK(gpio_config(&conf));

i2s_parallel_pin_config_t *lcd_conf = calloc(1, sizeof(i2s_parallel_pin_config_t));
lcd_conf->bit_width = dev->config.data_width;
for (int i = 0; i < lcd_conf->bit_width; ++i) {
ARG_CHECK(dev->config.data_io_num[i] != GPIO_NUM_NC, "dev->config.data_io_num is GPIO_NUM_NC");
lcd_conf->data[i] = dev->config.data_io_num[i];
}
lcd_conf->pin_clk = dev->config.ws_io_num;
i2s_parallel_init(lcd_conf, 4);
return ESP_OK;
}

inline void lcd_i2s_parallel_write_data16(const lcd_dev_t *dev, uint16_t data) {
ARG_ERROR_CHECK(dev != NULL, ERR_PARAM_NULL);
i2s_parallel_write_data16n(data, sizeof(uint16_t));
}

inline void lcd_i2s_parallel_write_data16n(const lcd_dev_t *dev, uint16_t data, size_t len) {
ARG_ERROR_CHECK(dev != NULL, ERR_PARAM_NULL);
i2s_parallel_write_data16n(data, len);
}

inline void lcd_i2s_parallel_write_datan(const lcd_dev_t *dev, const uint16_t *buf, size_t len) {
ARG_ERROR_CHECK(dev != NULL, ERR_PARAM_NULL);
ARG_ERROR_CHECK(buf != NULL, ERR_PARAM_NULL);
ARG_ERROR_CHECK(len > 0, ERR_PARAM_LE_ZERO);
LLOG(TAG, "[%s] buf: %p len: %2d", __FUNCTION__, buf, len);
i2s_parallel_write_data((uint8_t *) buf, len);
}
14 changes: 14 additions & 0 deletions main/driver/lcd/i2s_p.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
#ifndef HYDROPONICS_DRIVER_LCD_I2S_P_H
#define HYDROPONICS_DRIVER_LCD_I2S_P_H

#include "lcd.h"

esp_err_t lcd_i2s_parallel_init(lcd_dev_t *dev);

void lcd_i2s_parallel_write_data16(const lcd_dev_t *dev, uint16_t data);

void lcd_i2s_parallel_write_data16n(const lcd_dev_t *dev, uint16_t data, size_t len);

void lcd_i2s_parallel_write_datan(const lcd_dev_t *dev, const uint16_t *buf, size_t len);

#endif //HYDROPONICS_DRIVER_LCD_I2S_P_H
11 changes: 11 additions & 0 deletions main/driver/lcd/lcd.c
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@
#ifdef CONFIG_IDF_TARGET_ESP32
#define USE_I2S
#include "i2s.h"
#elif defined(CONFIG_IDF_TARGET_ESP32S2)
#define USE_I2S_PARALLEL
#include "i2s_parallel.h"
#else
#include "direct.h"
#endif
Expand All @@ -25,6 +28,8 @@ esp_err_t lcd_init(lcd_dev_t *dev) {

#ifdef USE_I2S
ESP_ERROR_CHECK(lcd_i2s_init(dev));
#elif defined(USE_I2S_PARALLEL)
ESP_ERROR_CHECK(lcd_i2s_parallel_init(dev));
#else
ESP_ERROR_CHECK(lcd_direct_init(dev));
#endif
Expand Down Expand Up @@ -74,6 +79,8 @@ esp_err_t lcd_init_registers(const lcd_dev_t *dev, const uint16_t *table, size_t
inline void lcd_write_data16(const lcd_dev_t *dev, uint16_t data) {
#ifdef USE_I2S
lcd_i2s_write_data16(dev, data);
#elif defined(USE_I2S_PARALLEL)
lcd_i2s_parallel_write_data16(dev, data);
#else
lcd_direct_write_data16(dev, data);
#endif
Expand All @@ -82,6 +89,8 @@ inline void lcd_write_data16(const lcd_dev_t *dev, uint16_t data) {
void lcd_write_data16n(const lcd_dev_t *dev, uint16_t data, size_t len) {
#ifdef USE_I2S
lcd_i2s_write_data16n(dev, data);
#elif defined(USE_I2S_PARALLEL)
lcd_i2s_parallel_write_data16n(dev, data, len);
#else
lcd_direct_write_data16n(dev, data, len);
#endif
Expand All @@ -90,6 +99,8 @@ void lcd_write_data16n(const lcd_dev_t *dev, uint16_t data, size_t len) {
inline void lcd_write_datan(const lcd_dev_t *dev, const uint16_t *buf, size_t len) {
#ifdef USE_I2S
lcd_i2s_write_datan(dev, buf, len);
#elif defined(USE_I2S_PARALLEL)
lcd_i2s_parallel_write_datan(dev, buf, len);
#else
lcd_direct_write_datan(dev, buf, len);
#endif
Expand Down

0 comments on commit 1536e1e

Please sign in to comment.