Files
Michael Sauer 1b0501179d build: W600-SDK importiert, Firmware-Build und Hello am EVK
Docker-Build mit gcc-arm-none-eabi 4.9-2014q4 (statt Homebrew-Cask) verwendet,
Dockerfile/tools/docker.sh dafür aus Task 13 vorgezogen. cmake/sdk.cmake schont
gcc 4.9 (kennt -fcommon/-Wno-error=... nicht). Zwei zusätzliche SDK-Stubs
(EarlyInit, pre_gpio_config) in src/sys/sdk_stubs.c ergänzt, siehe sdk/PATCHES.md.
2026-09-11 12:52:26 +02:00

585 lines
15 KiB
C

/**************************************************************************//**
* @file wm_i2s.c
* @author
* @version
* @date
* @brief
*
* Copyright (c) 2014 Winner Microelectronics Co., Ltd. All rights reserved.
*****************************************************************************/
#define TLS_CONFIG_I2S 1
#if TLS_CONFIG_I2S
#include "wm_i2s.h"
#include "string.h"
#include "wm_debug.h"
#include "wm_irq.h"
#include "wm_config.h"
#include "wm_mem.h"
#define I2S_CLK (160000000)
#define WM_I2S_FIFO_LEN (8)
static tls_i2s_port_t tls_i2s_port;
static void tls_i2s_fill_txfifo(void);
/**
* @brief This function is used to initial i2s port.
* @param[in] opts the i2s setting options,if this param is NULL,this function will use the default options.
* @retval
* - \ref WM_SUCCESS
* - \ref WM_FAILED
*
*/
int tls_i2s_port_init(tls_i2s_options_t *opts)
{
tls_i2s_options_t opt;
tls_i2s_port.regs = I2S;
if(NULL == opts)
{
memset(&opt, 0, sizeof(tls_i2s_options_t));
opt.data_width = I2S_CTRL_DATABIT_16;
opt.format = I2S_CTRL_FORMAT_I2S;
opt.stereo_mono = I2S_CTRL_STERO;
opt.sample_rate = 8000;
opt.tx_en = 1;
tls_i2s_config(&opt);
}
else
{
tls_i2s_config(opts);
}
/* enable i2s interrupt */
NVIC_ClearPendingIRQ(I2S_IRQn);
tls_irq_enable(I2S_INT);
return WM_SUCCESS;
}
/**
* @brief This function is used to initial i2s port.
* @param[in] opts the i2s setting options,if this param is NULL,this function will use the default options.
*
* @retval
*
* @note
*/
void tls_i2s_config(tls_i2s_options_t *opts)
{
tls_i2s_port.regs->CTRL &= ~(I2S_CTRL_FORMAT_MASK | \
I2S_CTRL_DATABIT_MASK | \
I2S_CTRL_STEREO_MONO_MASK);
tls_i2s_port.regs->CTRL = opts->data_width | opts->format | opts->stereo_mono;
if (opts->rx_en)
{
TLS_I2S_RX_ENABLE();
TLS_I2S_RX_FIFO_CLEAR();
}
if (opts->tx_en)
{
TLS_I2S_TX_ENABLE();
TLS_I2S_TX_FIFO_CLEAR();
}
tls_i2s_set_freq(opts->sample_rate);
}
/**
* @brief
* set the frequency of the i2s port.
*
* @param[in] freq
* the required frequency of the i2s module
*
* @retval
*
*/
void tls_i2s_set_freq(uint32_t freq)
{
uint32_t div;
uint32_t reg;
uint8_t width, stereo;
reg = I2S->CTRL;
width = (((reg>>4)&0x03)+1)<<3;
stereo = tls_bitband_read(HR_I2S_CTRL, I2S_CTRL_STEREO_MONO_Pos) ? 1:2;
div = (I2S_CLK + freq * width * stereo)/(freq * width * stereo) - 1;
*(volatile uint32_t *)HR_CLK_I2S_CTL &= ~0xFF00;
*(volatile uint32_t *)HR_CLK_I2S_CTL |= (uint32_t)div<<8;
}
/**
* @brief
* set the frequency of the i2s port.
*
* @param[in] freq
* the required frequency of the i2s module
* @param[in] exclk
* the frequency of the ext clock
*
* @retval
*
* @note
* this function will be called after tls_i2s_port_init function
*/
void wm_i2s_set_freq_exclk(uint32_t freq, uint32_t exclk)
{
uint32_t div;
uint32_t temp;
uint8_t wdwidth, stereo;
temp = I2S->CTRL;
wdwidth = (((temp>>4)&0x03)+1)<<3;
stereo = tls_bitband_read(HR_I2S_CTRL, 22) ? 1:2;
div = (exclk * 2 + freq * wdwidth * stereo)/(2* freq * wdwidth * stereo) - 1;
*(volatile uint32_t *)0x40000718 &= ~0x3FF00;
*(volatile uint32_t *)0x40000718 |= (uint32_t)div<<8;
*(volatile uint32_t *)0x40000718 |= 0x01;
}
void I2S_IRQHandler(void)
{
uint8_t rx_fifocnt;
/** LZC */
if (tls_bitband_read(HR_I2S_INT_SRC, I2S_FLAG_LZC_Pos) && !tls_bitband_read(HR_I2S_INT_MASK, I2S_FLAG_LZC_Pos))
{
tls_reg_write32(HR_I2S_INT_SRC, I2S_FLAG_LZC);
}
/** RZC */
if (tls_bitband_read(HR_I2S_INT_SRC, I2S_FLAG_RZC_Pos) && !tls_bitband_read(HR_I2S_INT_MASK, I2S_FLAG_RZC_Pos))
{
tls_reg_write32(HR_I2S_INT_SRC, I2S_FLAG_RZC);
}
/** Tx Done*/
if (tls_bitband_read(HR_I2S_INT_SRC, I2S_FLAG_TXDONE_Pos) && !tls_bitband_read(HR_I2S_INT_MASK, I2S_FLAG_TXDONE_Pos))
{
tls_reg_write32(HR_I2S_INT_SRC, I2S_FLAG_TXDONE);
}
/** TxTH*/
if (tls_bitband_read(HR_I2S_INT_SRC, I2S_FLAG_TXTH_Pos)&& !tls_bitband_read(HR_I2S_INT_MASK, I2S_FLAG_TXTH_Pos))
{
tls_reg_write32(HR_I2S_INT_SRC, I2S_FLAG_TXTH);
tls_i2s_fill_txfifo();
}
/** TXOV*/
if (tls_bitband_read(HR_I2S_INT_SRC, I2S_FLAG_TXOV_Pos) && !tls_bitband_read(HR_I2S_INT_MASK, I2S_FLAG_TXOV_Pos))
{
tls_reg_write32(HR_I2S_INT_SRC, I2S_FLAG_TXOV);
}
/** TXUD*/
if (tls_bitband_read(HR_I2S_INT_SRC, I2S_FLAG_TXUD_Pos) && !tls_bitband_read(HR_I2S_INT_MASK, I2S_FLAG_TXUD_Pos))
{
tls_reg_write32(HR_I2S_INT_SRC, I2S_FLAG_TXUD);
}
/** Rx Done*/
if (tls_bitband_read(HR_I2S_INT_SRC, I2S_FLAG_RXDONE_Pos) && !tls_bitband_read(HR_I2S_INT_MASK, I2S_FLAG_RXDONE_Pos))
{
tls_reg_write32(HR_I2S_INT_SRC, I2S_FLAG_RXDONE);
}
/** RxTH */
if (tls_bitband_read(HR_I2S_INT_SRC, I2S_FLAG_RXTH_Pos)&& !tls_bitband_read(HR_I2S_INT_MASK, I2S_FLAG_RXTH_Pos))
{
tls_reg_write32(HR_I2S_INT_SRC, I2S_FLAG_RXTH);
rx_fifocnt = tls_i2s_port.regs->INT_STATUS & I2S_RX_FIFO_CNT_MASK;
if (tls_i2s_port.rx_buf.buf != NULL)
{
while(rx_fifocnt-- > 0)
{
tls_i2s_port.rx_buf.buf[tls_i2s_port.rx_buf.index] = tls_i2s_port.regs->RX;
tls_i2s_port.rx_buf.index++;
}
if (tls_i2s_port.rx_buf.index >= (tls_i2s_port.rx_buf.len))
{
tls_i2s_port.rx_buf.buf = NULL;
tls_i2s_int_config(I2S_INT_MASK_RXTH, 0);
if (tls_i2s_port.rx_callback != NULL)
{
tls_i2s_port.rx_callback(tls_i2s_port.rx_buf.len);
}
tls_i2s_port.rx_buf.index = 0;
}
}
}
/** RXOV*/
if (tls_bitband_read(HR_I2S_INT_SRC, I2S_FLAG_RXOV_Pos) && !tls_bitband_read(HR_I2S_INT_MASK, I2S_FLAG_RXOV_Pos))
{
tls_reg_write32(HR_I2S_INT_SRC, I2S_FLAG_RXOV);
}
/** RXUD*/
if (tls_bitband_read(HR_I2S_INT_SRC, I2S_FLAG_RXUD_Pos) && !tls_bitband_read(HR_I2S_INT_MASK, I2S_FLAG_RXUD_Pos))
{
tls_reg_write32(HR_I2S_INT_SRC, I2S_FLAG_RXUD);
}
}
/**
* @brief
* This function is used to register i2s rx interrupt.
*
* @param[in] rx_callback
* is the i2s interrupt call back function.
*
* @retval
*
*/
void tls_i2s_rx_register( void (*rx_callback)(u16 len))
{
tls_i2s_port.rx_callback = rx_callback;
}
/**
* @brief
* This function is used to register i2s tx interrupt.
*
* @param[in] tx_callback
* is the i2s interrupt call back function.
*
* @retval
*
*/
void tls_i2s_tx_register(void (* tx_callback)(u16 len))
{
tls_i2s_port.tx_callback = tx_callback;
}
static void tls_i2s_fill_txfifo()
{
s8 fifo_left = 0;
if (tls_i2s_port.tx_buf.index < tls_i2s_port.tx_buf.len)
{
fifo_left = I2S_MAX_RXTXFIFO_LEVEL - ((tls_i2s_port.regs->INT_STATUS & I2S_TX_FIFO_CNT_MASK)>>4);
while (fifo_left--)
{
tls_i2s_port.regs->TX = *(uint32_t *)(tls_i2s_port.tx_buf.buf + tls_i2s_port.tx_buf.index);
tls_i2s_port.tx_buf.index ++;
}
if(tls_i2s_port.tx_buf.index >= tls_i2s_port.tx_buf.len)
{
while((tls_i2s_port.regs->INT_STATUS & I2S_TX_FIFO_CNT_MASK)>>4);
if(tls_i2s_port.tx_sem)
{
memset(&tls_i2s_port.tx_buf, 0, sizeof(tls_i2s_buf));
tls_os_sem_release(tls_i2s_port.tx_sem);
return;
}
if (tls_i2s_port.tx_callback)
{
tls_i2s_port.tx_callback(tls_i2s_port.tx_buf.len);
}
}
}
}
/**
* @brief
* This function is used to transfer data in blocking mode.
*
* @param[in] buf
* pointer to the transfering data.
*
* @param[in] len
* is the data length.
*
* @retval
*
*/
int tls_i2s_tx_block(uint32_t *buf, uint16_t len)
{
uint8_t err;
err = tls_os_sem_create(&tls_i2s_port.tx_sem, 0);
if (err != TLS_OS_SUCCESS)
{
TLS_DBGPRT_ERR("\ni2s tx sem create fail\n");
return WM_FAILED;
}
TLS_I2S_TX_FIFO_CLEAR();
tls_reg_write32(HR_I2S_INT_SRC, 0x1FF);
NVIC_ClearPendingIRQ(I2S_IRQn);
tls_i2s_port.tx_buf.buf = buf;
tls_i2s_port.tx_buf.len = len;
tls_i2s_port.tx_buf.index = 0;
tls_i2s_set_txth(5);
tls_i2s_int_config(I2S_INT_MASK_TXTH, 1);
TLS_I2S_TX_ENABLE();
TLS_I2S_ENABLE();
tls_os_sem_acquire(tls_i2s_port.tx_sem, 0);
tls_os_sem_delete(tls_i2s_port.tx_sem);
tls_i2s_port.tx_sem = NULL;
TLS_I2S_TX_DISABLE();
return WM_SUCCESS;
}
/**
* @brief
* This function is used to transfer data in non blocking mode.
*
* @param[in] buf
* is a buf for user data.
*
* @param[in] len
* is the data length.
*
* @param[in] tx_callback
* a function pointer,which will be called when the designated data sent
*
* @retval
*
* @note the \ref len in words
*/
int tls_i2s_tx_nonblock(uint32_t *buf, uint16_t len, void (*tx_callback)(u16 len))
{
tls_i2s_port.tx_buf.buf = (uint32_t *)buf;
tls_i2s_port.tx_buf.len = len;
tls_i2s_port.tx_buf.index = 0;
tls_i2s_set_txth(5);
tls_i2s_int_config(I2S_INT_MASK_TXTH, 1);
tls_i2s_tx_register( tx_callback);
TLS_I2S_TX_FIFO_CLEAR();
TLS_I2S_TX_ENABLE();
TLS_I2S_ENABLE();
return WM_SUCCESS;
}
/**
* @brief
* This function is used to receiver data in non blocking mode.
*
* @param[in] buf
* the buffer for storing the received data.
*
* @param[in] len
* the length of the data will be receiving
*
* @param[in] rx_callback
* a function pointer,which will be called when the demanded data have been received
*
* @retval
*
*/
int tls_i2s_rx_nonblock(uint32_t *buf, uint16_t len, void (*rx_callback)(u16 len))
{
tls_i2s_port.rx_buf.buf = buf;
tls_i2s_port.rx_buf.len = len;
tls_i2s_port.rx_buf.index = 0;
tls_i2s_set_rxth(5);
tls_i2s_int_config(I2S_INT_MASK_RXTH, 1);
tls_i2s_rx_register( rx_callback);
TLS_I2S_RX_FIFO_CLEAR();
NVIC_ClearPendingIRQ(I2S_IRQn);
TLS_I2S_RX_ENABLE();
TLS_I2S_ENABLE();
return WM_SUCCESS;
}
/**
* @brief
* This function is used to the tx DMA configuration of the I2S module.
*
* @param[in] dma_channel
* the free dma channel number which returned by calling function tls_dma_request()
*
* @param[in] addr
* point to the data buffer will be sent
*
* @param[in] len
* length of the data to be transfered in bytes
*
* @retval
*/
void tls_i2s_tx_dma_config(uint8_t dma_channel, uint32_t * addr, uint16_t len)
{
struct tls_dma_descriptor DmaDesc;
TLS_I2S_TX_DISABLE();
TLS_I2S_TX_FIFO_CLEAR();
/** Mask i2s txth interrupt*/
tls_i2s_set_txth(4);
tls_i2s_int_config(I2S_INT_MASK_TXTH, 0);
DmaDesc.src_addr = (unsigned int) addr;
DmaDesc.dest_addr = (unsigned int)HR_I2S_TX;
DmaDesc.dma_ctrl = TLS_DMA_DESC_CTRL_SRC_ADD_INC | TLS_DMA_DESC_CTRL_DATA_SIZE_WORD |
(len << 7);
DmaDesc.valid = TLS_DMA_DESC_VALID;
DmaDesc.next = NULL;
tls_dma_start(dma_channel, &DmaDesc, 0);
}
/**
* @brief
* datas transmit throuth DMA
*
* @param[in] addr
* point to the data buffer will be sent
* @param[in] len
* length of the data to be transfered in bytes
* @param[in] callback
* the callback function when datas transfer finished
*
* @retval
*
* @note the \ref len in bytes
*/
int tls_i2s_tx_dma(uint32_t * addr, uint16_t len, tls_i2s_callback callback)
{
uint8_t tx_channel;
TLS_I2S_TX_FIFO_CLEAR();
tls_reg_write32(HR_I2S_INT_SRC, 0x1FF);
NVIC_ClearPendingIRQ(I2S_IRQn);
TLS_I2S_TX_FIFO_CLEAR();
tx_channel = tls_dma_request(WM_I2S_TX_DMA_CHANNEL, TLS_DMA_FLAGS_CHANNEL_SEL(TLS_DMA_SEL_I2S_TX) | TLS_DMA_FLAGS_HARD_MODE);
if (tx_channel == 0)
{
return WM_FAILED;
}
if (tls_dma_stop(tx_channel))
{
return WM_FAILED;
}
tls_i2s_tx_dma_config(tx_channel, addr, len);
tls_dma_irq_register(tx_channel, callback, NULL, TLS_DMA_IRQ_TRANSFER_DONE);
TLS_I2S_TXDMA_ENABLE();
TLS_I2S_TX_ENABLE();
TLS_I2S_ENABLE();
return WM_SUCCESS;
}
/**
* @brief
* This function is used to the rx DMA configuration of the I2S module.
*
* @param[in] addr
* point to the storge buffer of the received data
*
* @param[in] len
* the storge buffer length in bytes
*
* @retval
*/
void tls_i2s_rx_dma_config(uint8_t dma_channel, uint32_t * addr, uint16_t len)
{
struct tls_dma_descriptor DmaDesc;
tls_i2s_int_config(I2S_INT_MASK_RXTH, 0);
TLS_I2S_RX_DISABLE();
TLS_I2S_RXDMA_DISABLE();
TLS_I2S_RX_FIFO_CLEAR();
/** Mask i2s rxth interrupt*/
tls_i2s_set_rxth(4);
tls_i2s_int_config(I2S_INT_MASK_RXTH, 0);
DmaDesc.src_addr = (int)HR_I2S_RX;
DmaDesc.dest_addr = (int)addr;
DmaDesc.dma_ctrl = TLS_DMA_DESC_CTRL_DEST_ADD_INC | TLS_DMA_DESC_CTRL_DATA_SIZE_WORD |
(len << 7);
DmaDesc.valid = TLS_DMA_DESC_VALID;
DmaDesc.next = NULL;
tls_dma_start(dma_channel, &DmaDesc, 0);
}
int tls_i2s_rx_dma(uint32_t * addr, uint16_t len, tls_i2s_callback callback)
{
uint8_t rx_channel;
rx_channel = tls_dma_request(WM_I2S_RX_DMA_CHANNEL, TLS_DMA_FLAGS_CHANNEL_SEL(TLS_DMA_SEL_I2S_RX) | TLS_DMA_FLAGS_HARD_MODE);
if (rx_channel == 0)
{
return WM_FAILED;
}
if (tls_dma_stop(rx_channel))
{
return WM_FAILED;
}
tls_i2s_rx_dma_config(rx_channel, addr, len);
tls_dma_irq_register(rx_channel, callback, NULL, TLS_DMA_IRQ_TRANSFER_DONE);
TLS_I2S_RXDMA_ENABLE();
NVIC_ClearPendingIRQ(I2S_IRQn);
TLS_I2S_RX_ENABLE();
TLS_I2S_ENABLE();
return WM_SUCCESS;
}
#if 1
uint8_t tst_flag = 0;
void tls_i2s_tx_dma_callback()
{
I2S->TX = 0x00;
while(!tls_bitband_read(HR_I2S_INT_SRC, 7));
TLS_I2S_TX_DISABLE();
DMA_CHNLCTRL_REG(WM_I2S_TX_DMA_CHANNEL) |= DMA_CHNL_CTRL_CHNL_OFF;
tls_dma_free(WM_I2S_TX_DMA_CHANNEL);
tst_flag = 1;
}
void tls_i2s_rx_dma_callback()
{
TLS_I2S_RX_DISABLE();
DMA_CHNLCTRL_REG(WM_I2S_RX_DMA_CHANNEL) |= DMA_CHNL_CTRL_CHNL_OFF;
tls_dma_free(WM_I2S_RX_DMA_CHANNEL);
tst_flag = 1;
}
void tls_i2s_rx_callback()
{
TLS_I2S_RX_DISABLE();
tst_flag = 1;
}
void tls_i2s_test()
{
uint32_t *ptr = NULL;
uint32_t len;
uint32_t * i2s_tx_test = tls_mem_alloc(1024);
tls_i2s_port.opts.data_width = I2S_CTRL_DATABIT_16;
tls_i2s_port.opts.format = I2S_CTRL_FORMAT_I2S;
tls_i2s_port.opts.tx_en = 1;
tls_i2s_port.opts.sample_rate = 8000;
tls_i2s_port.opts.stereo_mono = I2S_CTRL_STERO;
tls_i2s_port_init(&tls_i2s_port.opts);
if (i2s_tx_test == NULL)
{
return;
}
ptr = i2s_tx_test;
for (len = 0; len < 256; len++)
{
*ptr++ = 0xABCD0200 + len;
}
i2s_tx_test[255] = 0xABCD02FA;
tls_i2s_tx_dma(i2s_tx_test, 1024,tls_i2s_tx_dma_callback);
while(!tst_flag);
tst_flag = 0;
tls_i2s_tx_dma(i2s_tx_test, 1024,tls_i2s_tx_dma_callback);
while(!tst_flag);
tst_flag = 0;
}
#endif
#endif /** TLS_CONFIG_I2S */
/*** (C) COPYRIGHT 2014 Winner Microelectronics Co., Ltd. ***/