提交 0cfe2921 编写于 作者: W Wayne Lin

Update nuvoton RTT porting.

1. Fix porting driver.
2. Add NAU8822 audio codec driver.
3. Add NUC980 I2S porting driver.
上级 89c1c3f2
......@@ -166,33 +166,31 @@ static void nu_can_isr(nu_can_t can)
CAN_T *can_base = ((nu_can_t)can)->can_base;
/* Get interrupt event */
u32IIDRstatus = can_base->IIDR;
u32IIDRstatus = CAN_GET_INT_PENDING_STATUS(can_base);
if (u32IIDRstatus == 0x00008000) /* Check Status Interrupt Flag (Error status Int and Status change Int) */
{
/**************************/
/* Status Change interrupt*/
/**************************/
if (can_base->STATUS & CAN_STATUS_RXOK_Msk)
if (can_base->STATUS & CAN_STATUS_TXOK_Msk)
{
can_base->STATUS &= ~CAN_STATUS_TXOK_Msk; /* Clear Tx Ok status*/
#ifndef RT_CAN_USING_HDR
/* Using as Lisen,Loopback,Loopback+Lisen mode*/
rt_hw_can_isr(&can->dev, RT_CAN_EVENT_RX_IND);
rt_hw_can_isr(&can->dev, RT_CAN_EVENT_TX_DONE);
#endif
can_base->STATUS &= ~CAN_STATUS_RXOK_Msk; /* Clear Rx Ok status*/
rt_kprintf("RX OK INT\n") ;
//rt_kprintf("[%s]TX OK INT\n", can->name) ;
}
if (can_base->STATUS & CAN_STATUS_TXOK_Msk)
if (can_base->STATUS & CAN_STATUS_RXOK_Msk)
{
can_base->STATUS &= ~CAN_STATUS_RXOK_Msk; /* Clear Rx Ok status*/
#ifndef RT_CAN_USING_HDR
/* Using as Lisen,Loopback,Loopback+Lisen mode*/
rt_hw_can_isr(&can->dev, RT_CAN_EVENT_TX_DONE);
rt_hw_can_isr(&can->dev, RT_CAN_EVENT_RX_IND);
#endif
can_base->STATUS &= ~CAN_STATUS_TXOK_Msk; /* Clear Tx Ok status*/
rt_kprintf("TX OK INT\n") ;
//rt_kprintf("[%s]RX OK INT\n", can->name) ;
}
/**************************/
......@@ -200,12 +198,12 @@ static void nu_can_isr(nu_can_t can)
/**************************/
if (can_base->STATUS & CAN_STATUS_EWARN_Msk)
{
rt_kprintf("EWARN INT\n") ;
rt_kprintf("[%s]EWARN INT\n", can->name) ;
}
if (can_base->STATUS & CAN_STATUS_BOFF_Msk)
{
rt_kprintf("BOFF INT\n") ;
rt_kprintf("[%s]BUSOFF INT\n", can->name) ;
/* Do Init to release busoff pin */
can_base->CON = (CAN_CON_INIT_Msk | CAN_CON_CCE_Msk);
......@@ -215,17 +213,19 @@ static void nu_can_isr(nu_can_t can)
}
#ifdef RT_CAN_USING_HDR
/*Number of Message Object which caused the interrupt*/
else if (u32IIDRstatus != 0 && u32IIDRstatus <= 32)
else if (u32IIDRstatus > 0 && u32IIDRstatus <= 32)
{
rt_kprintf("=> Interrupt Pointer = %d\n", can_base->IIDR - 1);
rt_kprintf("=> Interrupt Pointer = %d\n", u32IIDRstatus - 1);
/*Message RAM 0~15 for CAN Tx using*/
if (u32IIDRstatus < 16)
if (u32IIDRstatus <= 16)
//rt_kprintf("[%s-Tx]IntId = %d\n", can->name, u32IIDRstatus);
rt_hw_can_isr(&can->dev, RT_CAN_EVENT_TX_DONE);
else /*Message RAM 16~31 for CAN Rx using*/
{
rt_hw_can_isr(&can->dev, (RT_CAN_EVENT_RX_IND | (((can_base->IIDR) - 1) << 8)));
//rt_kprintf("[%s-Rx]IntId = %d\n", can->name, u32IIDRstatus);
rt_hw_can_isr(&can->dev, (RT_CAN_EVENT_RX_IND | ((u32IIDRstatus - 1) << 8)));
}
CAN_CLR_INT_PENDING_BIT(can_base, ((can_base->IIDR) - 1)); /* Clear Interrupt Pending */
CAN_CLR_INT_PENDING_BIT(can_base, (u32IIDRstatus - 1)); /* Clear Interrupt Pending */
}
#endif
......@@ -316,7 +316,7 @@ static rt_err_t nu_can_control(struct rt_can_device *can, int cmd, void *arg)
CAN_T *can_base = ((nu_can_t)can)->can_base;
RT_ASSERT(can_base != RT_NULL);
/* Check baudrate */
/* Check baud rate */
RT_ASSERT(can->config.baud_rate != 0);
switch (cmd)
......@@ -359,8 +359,8 @@ static rt_err_t nu_can_control(struct rt_can_device *can, int cmd, void *arg)
}
break;
case RT_CAN_CMD_SET_FILTER:
#ifdef RT_CAN_USING_HDR
case RT_CAN_CMD_SET_FILTER:
filter_cfg = (struct rt_can_filter_config *)arg;
for (int i = 0; i < filter_cfg->count; i++)
......@@ -384,8 +384,9 @@ static rt_err_t nu_can_control(struct rt_can_device *can, int cmd, void *arg)
}
}
}
#endif
break;
#endif
case RT_CAN_CMD_SET_MODE:
argval = (rt_uint32_t) arg;
if (argval != RT_CAN_MODE_NORMAL && argval != RT_CAN_MODE_LISEN &&
......@@ -399,6 +400,7 @@ static rt_err_t nu_can_control(struct rt_can_device *can, int cmd, void *arg)
return nu_can_configure(can, &can->config);
}
break;
case RT_CAN_CMD_SET_BAUD:
argval = (rt_uint32_t) arg;
if (argval != CAN1MBaud && argval != CAN800kBaud && argval != CAN500kBaud && argval != CAN250kBaud &&
......@@ -412,6 +414,7 @@ static rt_err_t nu_can_control(struct rt_can_device *can, int cmd, void *arg)
return nu_can_configure(can, &can->config);
}
break;
case RT_CAN_CMD_SET_PRIV:
argval = (rt_uint32_t) arg;
if (argval != RT_CAN_MODE_PRIV && argval != RT_CAN_MODE_NOPRIV)
......@@ -503,7 +506,11 @@ static int nu_can_recvmsg(struct rt_can_device *can, void *buf, rt_uint32_t boxn
RT_ASSERT(buf != RT_NULL);
/* get data */
CAN_Receive(can_base, boxno, &tMsg);
if (CAN_Receive(can_base, boxno, &tMsg) == FALSE)
{
rt_kprintf("No available RX Msg.\n");
return -(RT_ERROR);
}
#ifdef RT_CAN_USING_HDR
/* Hardware filter messages are valid */
......@@ -522,6 +529,7 @@ static int nu_can_recvmsg(struct rt_can_device *can, void *buf, rt_uint32_t boxn
pmsg->ide = RT_CAN_EXTID;
pmsg->id = tMsg.Id;
}
if (tMsg.FrameType == CAN_DATA_FRAME)
{
/* Data frame */
......@@ -532,9 +540,10 @@ static int nu_can_recvmsg(struct rt_can_device *can, void *buf, rt_uint32_t boxn
/* Remote frame */
pmsg->rtr = RT_CAN_RTR;
}
pmsg->len = tMsg.DLC ;
rt_memcpy(pmsg->data, tMsg.Data, pmsg->len);
rt_memcpy(pmsg->data, tMsg.Data, pmsg->len);
return RT_EOK;
}
......
......@@ -3,12 +3,19 @@ from building import *
cwd = GetCurrentDir()
group = []
src = Split("""
audio_test.c
""")
CPPPATH = [cwd]
if GetDepend('NU_PKG_USING_NAU88L25'):
src = Split("""
acodec_nau88l25.c
audio_test.c
""")
CPPPATH = [cwd]
src += Glob('acodec_nau88l25.c')
group = DefineGroup('nu_pkgs_nau88l25', src, depend = [''], CPPPATH = CPPPATH)
elif GetDepend('NU_PKG_USING_NAU8822'):
src += Glob('acodec_nau8822.c')
group = DefineGroup('nu_pkgs_nau8822', src, depend = [''], CPPPATH = CPPPATH)
Return('group')
/**************************************************************************//**
*
* @copyright (C) 2019 Nuvoton Technology Corp. All rights reserved.
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author Notes
* 2020-12-12 Wayne First version
*
******************************************************************************/
#include <rtconfig.h>
#if defined(NU_PKG_USING_NAU8822)
#include <rtthread.h>
#include <rtdevice.h>
#include "acodec_nau8822.h"
#include "drv_i2s.h"
#define DBG_ENABLE
#define DBG_LEVEL DBG_LOG
#define DBG_SECTION_NAME "acodec.nau8822"
#define DBG_COLOR
#include <rtdbg.h>
#define DEF_NAU8822_ADDR 0x1A
static struct rt_i2c_bus_device *g_I2cBusDev = NULL;
S_NU_NAU8822_CONFIG *g_psCodecConfig = NULL;
static rt_err_t nau8822_init(void);
static rt_err_t nau8822_reset(void);
static rt_err_t nau8822_dsp_control(struct rt_audio_configure *config);
static rt_err_t nau8822_mixer_control(rt_uint32_t ui32Units, rt_uint32_t ui32Value);
static rt_err_t nau8822_mixer_query(rt_uint32_t ui32Units, rt_uint32_t *ui32Value);
nu_acodec_ops nu_acodec_ops_nau8822 =
{
.name = "NAU8822",
.role = NU_ACODEC_ROLE_SLAVE,
.config = { // Default settings.
.samplerate = 16000,
.channels = 2,
.samplebits = 16
},
.nu_acodec_init = nau8822_init,
.nu_acodec_reset = nau8822_reset,
.nu_acodec_dsp_control = nau8822_dsp_control,
.nu_acodec_mixer_control = nau8822_mixer_control,
.nu_acodec_mixer_query = nau8822_mixer_query
};
static void nau8822_delay_ms(rt_uint32_t nms)
{
rt_thread_mdelay(nms);
}
static int I2C_ReadNAU8822(uint8_t u8addr, uint16_t *pu16data)
{
struct rt_i2c_msg msgs[2];
uint8_t u8TxData = (u8addr << 1);
RT_ASSERT(g_I2cBusDev != NULL);
RT_ASSERT(pu16data != NULL);
msgs[0].addr = DEF_NAU8822_ADDR; /* Slave address */
msgs[0].flags = RT_I2C_WR; /* Write flag */
msgs[0].buf = (rt_uint8_t *)&u8TxData; /* Number of bytes sent */
msgs[0].len = sizeof(u8TxData); /* Number of bytes read */
msgs[1].addr = DEF_NAU8822_ADDR; /* Slave address */
msgs[1].flags = RT_I2C_RD; /* Read flag */
msgs[1].buf = (rt_uint8_t *)pu16data; /* Read data pointer */
msgs[1].len = 2; /* Number of bytes read */
if (rt_i2c_transfer(g_I2cBusDev, &msgs[0], 2) != 2)
{
return -RT_ERROR;
}
return RT_EOK;
}
static int I2C_WriteNAU8822(uint8_t u8addr, uint16_t u16data)
{
/* Write 9-bit data to 7-bit address register of NAU8822 */
struct rt_i2c_msg msg;
uint8_t au8TxData[2];
RT_ASSERT(g_I2cBusDev != NULL);
au8TxData[0] = (uint8_t)((u8addr << 1) | (u16data >> 8)); //u8addr [7:1] | u16data [8]
au8TxData[1] = (uint8_t)(u16data & 0x00FF); //data [7:0]
msg.addr = DEF_NAU8822_ADDR; /* Slave address */
msg.flags = RT_I2C_WR; /* Write flag */
msg.buf = (rt_uint8_t *)&au8TxData[0]; /* Slave register address */
msg.len = sizeof(au8TxData); /* Number of bytes sent */
if (g_I2cBusDev && rt_i2c_transfer(g_I2cBusDev, &msg, 1) != 1)
{
rt_kprintf("[Failed] addr=%x, data=%d\n", u8addr, u16data);
return -RT_ERROR;
}
{
/* Verify */
uint8_t au8RxData[2];
I2C_ReadNAU8822(u8addr, (uint16_t *)&au8RxData[0]);
rt_kprintf("Wrote addr %02x -> 0x%04x, read back -> 0x%02x%02x\n", u8addr, u16data, au8RxData[0], au8RxData[1]);
}
return RT_EOK;
}
static rt_err_t nau8822_probe(void)
{
return RT_EOK;
}
static rt_err_t nau8822_reset(void)
{
I2C_WriteNAU8822(0, 0x000); /* Reset all registers */
nau8822_delay_ms(30);
LOG_I("Software Reset.\n");
return RT_EOK;
}
static rt_err_t nau8822_dsp_config(rt_uint32_t ui32SamplRate, rt_uint8_t u8ChNum, rt_uint8_t u8SamplBit)
{
uint8_t bClkDiv;
uint8_t mClkDiv;
uint16_t u16AudIf = 0x010; /* I2S, 16-bit */
uint16_t u16ClkCtrl;
uint8_t u8WLEN;
if (ui32SamplRate > 48000)
return -RT_ERROR;
if (u8ChNum == 2)
{
u16AudIf = (u16AudIf & 0x1FE) | 0x0;
}
else
{
u16AudIf = (u16AudIf & 0x1FE) | 0x1;
}
/* Force to set Channel number to 2 */
u8ChNum = 2;
switch (u8SamplBit)
{
case 16:
u8WLEN = 0x0;
break;
case 20:
u8WLEN = 0x1;
break;
case 24:
u8WLEN = 0x2;
break;
case 32:
u8WLEN = 0x3;
break;
default:
LOG_E("sample rate not match!\n");
return -RT_ERROR;
}
u16AudIf = (u16AudIf & 0x19F) | (u8WLEN << 5);
I2C_WriteNAU8822(4, u16AudIf);
if (ui32SamplRate % 11025)
{
I2C_WriteNAU8822(36, 0x008); //12.288Mhz
I2C_WriteNAU8822(37, 0x00C);
I2C_WriteNAU8822(38, 0x093);
I2C_WriteNAU8822(39, 0x0E9);
mClkDiv = (48000 * 256 * u8ChNum) / (ui32SamplRate * 256);
bClkDiv = (ui32SamplRate * 256) / (ui32SamplRate * u8ChNum * u8SamplBit);
}
else
{
I2C_WriteNAU8822(36, 0x007); //11.2896Mhz
I2C_WriteNAU8822(37, 0x021);
I2C_WriteNAU8822(38, 0x161);
I2C_WriteNAU8822(39, 0x026);
mClkDiv = (44100 * 256 * u8ChNum) / (ui32SamplRate * 256);
bClkDiv = (ui32SamplRate * 256) / (ui32SamplRate * u8ChNum * u8SamplBit);
}
switch (mClkDiv)
{
case 1:
mClkDiv = 0;
break;
case 2:
mClkDiv = 2;
break;
case 3:
mClkDiv = 3;
break;
case 4:
mClkDiv = 4;
break;
case 6:
mClkDiv = 5;
break;
case 8:
mClkDiv = 6;
break;
case 12:
mClkDiv = 7;
break;
default:
LOG_E("mclk divider not match!\n");
mClkDiv = 0;
return -RT_ERROR;
}
switch (bClkDiv)
{
case 1:
bClkDiv = 0;
break;
case 2:
bClkDiv = 1;
break;
case 4:
bClkDiv = 2;
break;
case 8:
bClkDiv = 3;
break;
case 16:
bClkDiv = 4;
break;
case 32:
bClkDiv = 5;
break;
default:
LOG_E("bclk divider not match!\n");
bClkDiv = 0;
return -RT_ERROR;
}
u16ClkCtrl = (1 << 8) | (1 << 0); //Use internal PLL, FS/BCLK
u16ClkCtrl = (u16ClkCtrl & 0x11F) | (mClkDiv << 5);
u16ClkCtrl = (u16ClkCtrl & 0x1E3) | (bClkDiv << 2);
I2C_WriteNAU8822(6, u16ClkCtrl);
return RT_EOK;
}
static rt_err_t nau8822_init(void)
{
//input source is MIC
I2C_WriteNAU8822(1, 0x03F);
I2C_WriteNAU8822(2, 0x1BF); /* Enable L/R Headphone, ADC Mix/Boost, ADC */
I2C_WriteNAU8822(3, 0x07F); /* Enable L/R main mixer, DAC */
I2C_WriteNAU8822(4, 0x010); /* 16-bit word length, I2S format, Stereo */
I2C_WriteNAU8822(5, 0x000); /* Companding control and loop back mode (all disable) */
nau8822_delay_ms(30);
if (nu_acodec_ops_nau8822.role == NU_ACODEC_ROLE_SLAVE)
{
I2C_WriteNAU8822(6, 0x1AD); /* Divide by 6, 16K */
I2C_WriteNAU8822(7, 0x006); /* 16K for internal filter coefficients */
}
I2C_WriteNAU8822(10, 0x008); /* DAC soft mute is disabled, DAC oversampling rate is 128x */
I2C_WriteNAU8822(14, 0x108); /* ADC HP filter is disabled, ADC oversampling rate is 128x */
I2C_WriteNAU8822(15, 0x1EF); /* ADC left digital volume control */
I2C_WriteNAU8822(16, 0x1EF); /* ADC right digital volume control */
I2C_WriteNAU8822(44, 0x033); /* LMICN/LMICP is connected to PGA */
I2C_WriteNAU8822(49, 0x042);
I2C_WriteNAU8822(50, 0x001); /* Left DAC connected to LMIX */
I2C_WriteNAU8822(51, 0x001); /* Right DAC connected to RMIX */
nu_acodec_ops_nau8822.config.samplerate = 16000;
nu_acodec_ops_nau8822.config.channels = 2;
nu_acodec_ops_nau8822.config.samplebits = 16;
LOG_I("Initialized done.\n");
return RT_EOK;
}
static rt_err_t nau8822_dsp_control(struct rt_audio_configure *config)
{
rt_err_t result = RT_EOK;
RT_ASSERT(config != RT_NULL);
if (rt_memcmp((void *)config, (void *)&nu_acodec_ops_nau8822.config, sizeof(struct rt_audio_configure)) != 0)
{
if ((result = nau8822_dsp_config(config->samplerate, config->channels, config->samplebits)) == RT_EOK)
rt_memcpy((void *)&nu_acodec_ops_nau8822.config, (void *)config, sizeof(struct rt_audio_configure)) ;
}
return result;
}
static rt_err_t nau8822_mixer_control(rt_uint32_t ui32Units, rt_uint32_t ui32Value)
{
switch (ui32Units)
{
case AUDIO_MIXER_MUTE:
{
uint16_t u16Data;
I2C_ReadNAU8822(10, &u16Data);
if (ui32Value)
{
I2C_WriteNAU8822(10, u16Data | (1 << 6));
}
else
{
I2C_WriteNAU8822(10, u16Data & ~(1 << 6));
}
}
break;
case AUDIO_MIXER_VOLUME:
{
uint8_t u8DACGAIN = 256 * ui32Value / 100;
I2C_WriteNAU8822(11, u8DACGAIN);
I2C_WriteNAU8822(12, u8DACGAIN);
}
break;
case AUDIO_MIXER_QUERY:
case AUDIO_MIXER_BASS:
case AUDIO_MIXER_MID:
case AUDIO_MIXER_TREBLE:
case AUDIO_MIXER_EQUALIZER:
case AUDIO_MIXER_LINE:
case AUDIO_MIXER_DIGITAL:
case AUDIO_MIXER_MIC:
case AUDIO_MIXER_VITURAL:
case AUDIO_MIXER_EXTEND:
default:
return -RT_ERROR;
}
return RT_EOK;
}
static rt_err_t nau8822_mixer_query(rt_uint32_t ui32Units, rt_uint32_t *pui32Value)
{
RT_ASSERT(pui32Value != RT_NULL);
rt_uint16_t u16RV = 0;
switch (ui32Units)
{
case AUDIO_MIXER_QUERY:
*pui32Value = AUDIO_MIXER_VOLUME | AUDIO_MIXER_MUTE;
break;
case AUDIO_MIXER_MUTE:
I2C_ReadNAU8822(10, (uint16_t *)&u16RV);
if (u16RV & (1 << 6))
*pui32Value = 1;
else
*pui32Value = 0;
break;
case AUDIO_MIXER_VOLUME:
I2C_ReadNAU8822(11, (uint16_t *)&u16RV);
*pui32Value = u16RV * 100 / 256;
break;
case AUDIO_MIXER_BASS:
case AUDIO_MIXER_MID:
case AUDIO_MIXER_TREBLE:
case AUDIO_MIXER_EQUALIZER:
case AUDIO_MIXER_LINE:
case AUDIO_MIXER_DIGITAL:
case AUDIO_MIXER_MIC:
case AUDIO_MIXER_VITURAL:
case AUDIO_MIXER_EXTEND:
default:
return -RT_ERROR;
}
return RT_EOK;
}
int nu_hw_nau8822_init(S_NU_NAU8822_CONFIG *psCodecConfig)
{
RT_ASSERT(psCodecConfig != RT_NULL);
struct rt_i2c_bus_device *psI2cBusDev;
struct rt_audio_device *psAudioDev;
nu_i2s_t psNuI2s;
/* Find I2C bus */
psI2cBusDev = (struct rt_i2c_bus_device *)rt_device_find(psCodecConfig->i2c_bus_name);
if (psI2cBusDev == RT_NULL)
{
LOG_E("Can't found I2C bus - %s..!\n", psCodecConfig->i2c_bus_name);
goto exit_rt_hw_nau8822_init;
}
/* Find I2S bus */
psAudioDev = (struct rt_audio_device *)rt_device_find(psCodecConfig->i2s_bus_name);
if (psAudioDev == RT_NULL)
{
LOG_E("Can't found I2S bus - %s ..!\n", psCodecConfig->i2s_bus_name);
goto exit_rt_hw_nau8822_init;
}
if (nau8822_probe() != RT_EOK)
{
LOG_E("Can't found audio codec..!\n");
goto exit_rt_hw_nau8822_init;
}
/* Store this board setting. */
g_psCodecConfig = psCodecConfig;
g_I2cBusDev = psI2cBusDev;
/* Get NuI2S device instance. */
psNuI2s = (nu_i2s_t)psAudioDev;
/* Register Acodec Ops */
psNuI2s->AcodecOps = &nu_acodec_ops_nau8822;
/* Use Acodec default settings. */
rt_memcpy(&psNuI2s->config, &nu_acodec_ops_nau8822.config, sizeof(struct rt_audio_configure));
return RT_EOK;
exit_rt_hw_nau8822_init:
return -RT_ERROR;
}
#endif //#if defined(NU_PKG_USING_NAU8822)
/**************************************************************************//**
*
* @copyright (C) 2019 Nuvoton Technology Corp. All rights reserved.
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author Notes
* 2020-12-12 Wayne First version
*
******************************************************************************/
#ifndef __ACODEC_NAU8822_H__
#define __ACODEC_NAU8822_H__
#include <rtdevice.h>
typedef struct
{
char *i2c_bus_name;
char *i2s_bus_name;
rt_int32_t pin_phonejack_en;
rt_int32_t pin_phonejack_det;
} S_NU_NAU8822_CONFIG;
int nu_hw_nau8822_init(S_NU_NAU8822_CONFIG *psCodecConfig);
#endif /* __ACODEC_NAU8822_H__ */
......@@ -24,6 +24,11 @@ menu "Nuvoton Packages Config"
select BSP_USING_I2C
default n
config NU_PKG_USING_NAU8822
bool "NAU8822 Audio Codec."
select BSP_USING_I2C
default n
config NU_PKG_USING_ILI9341
bool "ILI9341 LCD Panel"
select BSP_USING_GPIO
......
......@@ -27,6 +27,7 @@
#include "nu_pwm.h"
#include "nu_crypto.h"
#include "nu_can.h"
#include "nu_i2s.h"
#include "nu_sys.h"
......
......@@ -111,6 +111,7 @@ extern "C"
*/
int32_t i2sOpen(void);
void i2sClose(void);
void i2sInit(void);
int32_t i2sIoctl(uint32_t cmd, uint32_t arg0, uint32_t arg1);
void i2sSetSampleRate(uint32_t u32SourceClockRate, uint32_t u32SampleRate, uint32_t u32DataBit, uint32_t u32Channel);
......
......@@ -202,9 +202,6 @@ void i2sClose(void)
// reset i2s interface
outpw(REG_SYS_AHBIPRST, inpw(REG_SYS_AHBIPRST) | (1 << 8));
outpw(REG_SYS_AHBIPRST, inpw(REG_SYS_AHBIPRST) & ~(1 << 8));
// disable i2s engine clock
outpw(REG_CLK_HCLKEN, inpw(REG_CLK_HCLKEN) & ~(1 << 24));
}
/**
......
......@@ -11,6 +11,7 @@
| EMAC | RT_Device_Class_NetIf | ***e[0-1]*** |
| GPIO | RT_Device_Class_Miscellaneous (Pin) | ***gpio*** |
| I2C | RT_Device_Class_I2CBUS | ***i2c[0-3]*** |
| I2S | RT_Device_Class_Sound | ***sound0*** |
| PDMA | N/A | ***N/A*** |
| QSPI | RT_Device_Class_SPIBUS | ***qspi[0]*** |
| RTC | RT_Device_Class_RTC | ***rtc*** |
......@@ -25,4 +26,4 @@
## Resources
* [Download NUC980 TRM][1]
[1]: https://www.nuvoton.com/resource-download.jsp?tp_GUID=DA05-M480
[1]: https://www.nuvoton.com/resource-download.jsp?tp_GUID=DA05-NUC980-1
......@@ -22,12 +22,7 @@ config SOC_SERIES_NUC980
config NU_PDMA_MEMFUN_ACTOR_MAX
int "Specify maximum mem actor for memfun"
range 1 4
default 4
config NU_PDMA_SGTBL_POOL_SIZE
int "Specify maximum scatter-gather pool size"
range 1 32
default 16
default 2
endif
config BSP_USING_GPIO
......
......@@ -137,33 +137,31 @@ static void nu_can_isr(int vector, void *param)
CAN_T *base = psNuCAN->base;
/* Get interrupt event */
u32IIDRstatus = base->IIDR;
u32IIDRstatus = CAN_GET_INT_PENDING_STATUS(base);
if (u32IIDRstatus == 0x00008000) /* Check Status Interrupt Flag (Error status Int and Status change Int) */
{
/**************************/
/* Status Change interrupt*/
/**************************/
if (base->STATUS & CAN_STATUS_RXOK_Msk)
if (base->STATUS & CAN_STATUS_TXOK_Msk)
{
base->STATUS &= ~CAN_STATUS_TXOK_Msk; /* Clear Tx Ok status*/
//rt_kprintf("%s: TX\n", psNuCAN->name) ;
#ifndef RT_CAN_USING_HDR
/* Using as Lisen,Loopback,Loopback+Lisen mode*/
rt_hw_can_isr(&psNuCAN->dev, RT_CAN_EVENT_RX_IND);
rt_hw_can_isr(&psNuCAN->dev, RT_CAN_EVENT_TX_DONE);
#endif
base->STATUS &= ~CAN_STATUS_RXOK_Msk; /* Clear Rx Ok status*/
rt_kprintf("%s: RX\n", psNuCAN->name) ;
}
if (base->STATUS & CAN_STATUS_TXOK_Msk)
if (base->STATUS & CAN_STATUS_RXOK_Msk)
{
base->STATUS &= ~CAN_STATUS_RXOK_Msk; /* Clear Rx Ok status*/
//rt_kprintf("%s: RX\n", psNuCAN->name) ;
#ifndef RT_CAN_USING_HDR
/* Using as Lisen,Loopback,Loopback+Lisen mode*/
rt_hw_can_isr(&psNuCAN->dev, RT_CAN_EVENT_TX_DONE);
rt_hw_can_isr(&psNuCAN->dev, RT_CAN_EVENT_RX_IND);
#endif
base->STATUS &= ~CAN_STATUS_TXOK_Msk; /* Clear Tx Ok status*/
rt_kprintf("%s: TX\n", psNuCAN->name) ;
}
/**************************/
......@@ -183,22 +181,29 @@ static void nu_can_isr(int vector, void *param)
base->CON &= (~(CAN_CON_INIT_Msk | CAN_CON_CCE_Msk));
while (base->CON & CAN_CON_INIT_Msk);
}
if (base->STATUS & CAN_STATUS_LEC_Msk)
{
rt_kprintf("[%s] Last Error Code %03x\n", psNuCAN->name, base->STATUS & CAN_STATUS_LEC_Msk) ;
}
}
#ifdef RT_CAN_USING_HDR
/*Number of Message Object which caused the interrupt*/
else if (u32IIDRstatus != 0 && u32IIDRstatus <= 32)
/*IntId: 0x0001-0x0020, Number of Message Object which caused the interrupt.*/
else if (u32IIDRstatus > 0 && u32IIDRstatus <= 32)
{
rt_kprintf("=> Interrupt Pointer = %d\n", base->IIDR - 1);
/*Message RAM 0~15 for CAN Tx using*/
if (u32IIDRstatus < 16)
if (u32IIDRstatus <= 16)
{
//rt_kprintf("[%s-Tx]IntId = %d\n", psNuCAN->name, u32IIDRstatus);
rt_hw_can_isr(&psNuCAN->dev, RT_CAN_EVENT_TX_DONE);
}
else /*Message RAM 16~31 for CAN Rx using*/
{
rt_hw_can_isr(&psNuCAN->dev, (RT_CAN_EVENT_RX_IND | (((base->IIDR) - 1) << 8)));
//rt_kprintf("[%s-Rx]IntId = %d\n", psNuCAN->name, u32IIDRstatus);
rt_hw_can_isr(&psNuCAN->dev, (RT_CAN_EVENT_RX_IND | ((u32IIDRstatus - 1) << 8)));
}
CAN_CLR_INT_PENDING_BIT(base, ((base->IIDR) - 1)); /* Clear Interrupt Pending */
CAN_CLR_INT_PENDING_BIT(base, (u32IIDRstatus - 1)); /* Clear Interrupt Pending */
}
#endif
......@@ -362,6 +367,7 @@ static rt_err_t nu_can_control(struct rt_can_device *can, int cmd, void *arg)
}
break;
#endif
case RT_CAN_CMD_SET_MODE:
argval = (rt_uint32_t) arg;
if (argval != RT_CAN_MODE_NORMAL && argval != RT_CAN_MODE_LISEN &&
......@@ -436,8 +442,10 @@ static int nu_can_sendmsg(struct rt_can_device *can, const void *buf, rt_uint32_
RT_ASSERT(base != RT_NULL);
RT_ASSERT(buf != RT_NULL);
/* Check the parameters */
RT_ASSERT(IS_CAN_DLC(pmsg->len));
/* Standard ID (11 bits)*/
if (pmsg->ide == RT_CAN_STDID)
{
......@@ -484,7 +492,11 @@ static int nu_can_recvmsg(struct rt_can_device *can, void *buf, rt_uint32_t boxn
RT_ASSERT(buf != RT_NULL);
/* get data */
CAN_Receive(base, boxno, &tMsg);
if (CAN_Receive(base, boxno, &tMsg) == FALSE)
{
rt_kprintf("No available RX Msg.\n");
return -(RT_ERROR);
}
#ifdef RT_CAN_USING_HDR
/* Hardware filter messages are valid */
......@@ -503,6 +515,7 @@ static int nu_can_recvmsg(struct rt_can_device *can, void *buf, rt_uint32_t boxn
pmsg->ide = RT_CAN_EXTID;
pmsg->id = tMsg.Id;
}
if (tMsg.FrameType == CAN_DATA_FRAME)
{
/* Data frame */
......@@ -513,9 +526,10 @@ static int nu_can_recvmsg(struct rt_can_device *can, void *buf, rt_uint32_t boxn
/* Remote frame */
pmsg->rtr = RT_CAN_RTR;
}
pmsg->len = tMsg.DLC ;
rt_memcpy(pmsg->data, tMsg.Data, pmsg->len);
rt_memcpy(pmsg->data, tMsg.Data, pmsg->len);
return RT_EOK;
}
......
......@@ -15,8 +15,8 @@
#if defined(BSP_USING_I2S)
#include <rtdevice.h>
#include <drv_pdma.h>
#include <drv_i2s.h>
#include <NuMicro.h>
/* Private define ---------------------------------------------------------------*/
#define DBG_ENABLE
......@@ -25,6 +25,28 @@
#define DBG_COLOR
#include <rtdbg.h>
#define I2S_RSR_R_DMA_RIA_IRQ_Pos (0)
#define I2S_RSR_R_DMA_RIA_IRQ_Msk (1<<I2S_RSR_R_DMA_RIA_IRQ_Pos)
#define I2S_RSR_R_DMA_RIA_SN_Pos (5)
#define I2S_RSR_R_DMA_RIA_SN_Msk (7<<I2S_RSR_R_DMA_RIA_SN_Pos)
#define I2S_GLBCON_P_DMA_IRQ_Pos (10)
#define I2S_GLBCON_P_DMA_IRQ_Msk (1<<I2S_GLBCON_P_DMA_IRQ_Pos)
#define I2S_GLBCON_R_DMA_IRQ_Pos (11)
#define I2S_GLBCON_R_DMA_IRQ_Msk (1<<I2S_GLBCON_R_DMA_IRQ_Pos)
#define I2S_PSP_P_DMA_RIA_I_Pos (0)
#define I2S_PSP_P_DMA_RIA_I_Msk (1<<I2S_PSP_P_DMA_RIA_I_Pos)
#define I2S_PSP_DMA_DATA_ZERO_IRQ_Pos (3)
#define I2S_PSP_DMA_DATA_ZERO_IRQ_Msk (1<<I2S_PSP_DMA_DATA_ZERO_IRQ_Pos)
#define I2S_PSP_DMA_CNTER_IRQ_Pos (4)
#define I2S_PSP_DMA_CNTER_IRQ_Msk (1<<I2S_PSP_DMA_CNTER_IRQ_Pos)
#define I2S_RESET_PLAY_Pos (5)
#define I2S_RESET_PLAY_Msk (1<<I2S_RESET_PLAY_Pos)
#define I2S_RESET_RECORD_Pos (6)
#define I2S_RESET_RECORD_Msk (1<<I2S_RESET_RECORD_Pos)
/* Private functions ------------------------------------------------------------*/
static rt_err_t nu_i2s_getcaps(struct rt_audio_device *audio, struct rt_audio_caps *caps);
static rt_err_t nu_i2s_configure(struct rt_audio_device *audio, struct rt_audio_caps *caps);
......@@ -39,109 +61,73 @@ rt_err_t nu_i2s_acodec_register(nu_acodec_ops_t);
static struct nu_i2s g_nu_i2s_dev =
{
.name = "sound0",
.i2s_base = I2S0,
.i2s_rst = I2S0_RST,
.i2s_dais = {
[NU_I2S_DAI_PLAYBACK] = {
.pdma_perp = PDMA_I2S0_TX,
},
[NU_I2S_DAI_CAPTURE] = {
.pdma_perp = PDMA_I2S0_RX,
}
}
.irqn = IRQ_I2S,
.rstidx = I2SRST,
.clkidx = I2SCKEN,
};
static void nu_pdma_i2s_rx_cb(void *pvUserData, uint32_t u32EventFilter)
static void nu_i2s_isr(int vector, void *param)
{
nu_i2s_t psNuI2s = (nu_i2s_t)pvUserData;
nu_i2s_dai_t psNuI2sDai;
RT_ASSERT(psNuI2s != RT_NULL);
psNuI2sDai = &psNuI2s->i2s_dais[NU_I2S_DAI_CAPTURE];
nu_i2s_t psNuI2s = (nu_i2s_t)param;
volatile uint32_t u32RegAudCtl = inpw(REG_I2S_CON);
if (u32EventFilter & NU_PDMA_EVENT_TRANSFER_DONE)
if (u32RegAudCtl & I2S_GLBCON_P_DMA_IRQ_Msk)
{
// Report a buffer ready.
rt_uint8_t *pbuf_old = &psNuI2sDai->fifo[psNuI2sDai->fifo_block_idx * NU_I2S_DMA_BUF_BLOCK_SIZE] ;
psNuI2sDai->fifo_block_idx = (psNuI2sDai->fifo_block_idx + 1) % NU_I2S_DMA_BUF_BLOCK_NUMBER;
/* Report upper layer. */
rt_audio_rx_done(&psNuI2s->audio, pbuf_old, NU_I2S_DMA_BUF_BLOCK_SIZE);
}
}
volatile uint32_t u32RegPlayStatus = inpw(REG_I2S_PSR);
outpw(REG_I2S_CON, u32RegAudCtl | I2S_GLBCON_P_DMA_IRQ_Msk); //Clear TX INT
static void nu_pdma_i2s_tx_cb(void *pvUserData, uint32_t u32EventFilter)
{
nu_i2s_t psNuI2s = (nu_i2s_t)pvUserData;
nu_i2s_dai_t psNuI2sDai;
if (u32RegPlayStatus & I2S_PSP_DMA_CNTER_IRQ_Msk)
{
outpw(REG_I2S_PSR, I2S_PSP_DMA_CNTER_IRQ_Msk);
rt_kprintf("\ndebug:DMA_COUNTER_IRQ occur\n");
}
RT_ASSERT(psNuI2s != RT_NULL);
psNuI2sDai = &psNuI2s->i2s_dais[NU_I2S_DAI_PLAYBACK];
if (u32RegPlayStatus & I2S_PSP_DMA_DATA_ZERO_IRQ_Msk)
{
outpw(REG_I2S_PSR, I2S_PSP_DMA_DATA_ZERO_IRQ_Msk);
rt_kprintf("\ndebug:DMA_DATA_ZERO_IRQ occur\n");
}
if (u32EventFilter & NU_PDMA_EVENT_TRANSFER_DONE)
{
rt_audio_tx_complete(&psNuI2s->audio);
psNuI2sDai->fifo_block_idx = (psNuI2sDai->fifo_block_idx + 1) % NU_I2S_DMA_BUF_BLOCK_NUMBER;
if (u32RegPlayStatus & I2S_PSP_P_DMA_RIA_I_Msk)
{
/* Clear Playback status of DMA reach indicate address interrupt. */
outpw(REG_I2S_PSR, I2S_PSP_P_DMA_RIA_I_Msk);
rt_audio_tx_complete(&psNuI2s->audio);
}
}
}
static rt_err_t nu_i2s_pdma_sc_config(nu_i2s_t psNuI2s, E_NU_I2S_DAI dai)
{
rt_err_t result = RT_EOK;
I2S_T *i2s_base;
nu_i2s_dai_t psNuI2sDai;
int i;
uint32_t u32Src, u32Dst;
nu_pdma_cb_handler_t pfm_pdma_cb;
if (u32RegAudCtl & I2S_GLBCON_R_DMA_IRQ_Msk)
{
volatile uint32_t u32RegRecordStatus = inpw(REG_I2S_RSR);
RT_ASSERT(psNuI2s != RT_NULL);
outpw(REG_I2S_CON, u32RegAudCtl | I2S_GLBCON_R_DMA_IRQ_Msk); //Clear RX INT
/* Get base address of i2s register */
i2s_base = psNuI2s->i2s_base;
psNuI2sDai = &psNuI2s->i2s_dais[dai];
/* Record DMA Reach Indicative Address Interrupt Request Bit */
/* 0 = Record DMA address does not reach the indicative address by R_DMA_IRQ_SEL */
/* 1 = Record DMA address does reach the indicative address by R_DMA_IRQ_SEL */
/* Note: This bit is readable, and can only be cleared by writing '1' to it. */
if (u32RegRecordStatus & I2S_RSR_R_DMA_RIA_IRQ_Msk)
{
nu_i2s_dai_t psNuI2sDai = &psNuI2s->i2s_dais[NU_I2S_DAI_CAPTURE];
switch ((int)dai)
{
case NU_I2S_DAI_PLAYBACK:
pfm_pdma_cb = nu_pdma_i2s_tx_cb;
u32Src = (uint32_t)&psNuI2sDai->fifo[0];
u32Dst = (uint32_t)&i2s_base->TXFIFO;
break;
/*
Record DMA Reach Indicative Address Section Number Bit (Read Only)
R_DMA_IRQ_SEL (I2S_GLBCON[15:14]) = 01, R_DMA_RIA_SN[2:0]= 1, 0.
R_DMA_IRQ_SEL (I2S_GLBCON[15:14]) = 10, R_DMA_RIA_SN[2:0]= 1, 2, 3, 0.
R_DMA_IRQ_SEL (I2S_GLBCON[15:14]) = 11, R_DMA_RIA_SN[2:0]= 1, 2, 3, 4, 5, 6, 7, 0.
*/
uint8_t u8FifoBlockIdx = (u32RegRecordStatus & I2S_RSR_R_DMA_RIA_SN_Msk) >> I2S_RSR_R_DMA_RIA_SN_Pos;
case NU_I2S_DAI_CAPTURE:
pfm_pdma_cb = nu_pdma_i2s_rx_cb;
u32Src = (uint32_t)&i2s_base->RXFIFO;
u32Dst = (uint32_t)&psNuI2sDai->fifo[0];
break;
rt_uint8_t *pbuf = (uint8_t *)((uint32_t)&psNuI2sDai->fifo[u8FifoBlockIdx * NU_I2S_DMA_BUF_BLOCK_SIZE] | NONCACHEABLE);
default:
return -RT_EINVAL;
}
/* Clear Record status of DMA reach indicate address interrupt. */
outpw(REG_I2S_RSR, I2S_RSR_R_DMA_RIA_IRQ_Msk);
result = nu_pdma_callback_register(psNuI2sDai->pdma_chanid,
pfm_pdma_cb,
(void *)psNuI2s,
NU_PDMA_EVENT_TRANSFER_DONE);
RT_ASSERT(result == RT_EOK);
for (i = 0; i < NU_I2S_DMA_BUF_BLOCK_NUMBER; i++)
{
/* Setup dma descriptor entry */
result = nu_pdma_desc_setup(psNuI2sDai->pdma_chanid, // Channel ID
psNuI2sDai->pdma_descs[i], // this descriptor
32, // 32-bits
(dai == NU_I2S_DAI_PLAYBACK) ? u32Src + (i * NU_I2S_DMA_BUF_BLOCK_SIZE) : u32Src, //Memory or RXFIFO
(dai == NU_I2S_DAI_PLAYBACK) ? u32Dst : u32Dst + (i * NU_I2S_DMA_BUF_BLOCK_SIZE), //TXFIFO or Memory
(int32_t)NU_I2S_DMA_BUF_BLOCK_SIZE / 4, // Transfer count
psNuI2sDai->pdma_descs[(i + 1) % NU_I2S_DMA_BUF_BLOCK_NUMBER]); // Next descriptor
RT_ASSERT(result == RT_EOK);
/* Report upper layer. */
rt_audio_rx_done(&psNuI2s->audio, pbuf, NU_I2S_DMA_BUF_BLOCK_SIZE);
}
}
/* Assign head descriptor */
result = nu_pdma_sg_transfer(psNuI2sDai->pdma_chanid, psNuI2sDai->pdma_descs[0], 0);
RT_ASSERT(result == RT_EOK);
return result;
}
static rt_bool_t nu_i2s_capacity_check(struct rt_audio_configure *pconfig)
......@@ -150,8 +136,7 @@ static rt_bool_t nu_i2s_capacity_check(struct rt_audio_configure *pconfig)
{
case 8:
case 16:
/* case 24: PDMA constrain */
case 32:
case 24:
break;
default:
goto exit_nu_i2s_capacity_check;
......@@ -179,7 +164,6 @@ static rt_err_t nu_i2s_dai_setup(nu_i2s_t psNuI2s, struct rt_audio_configure *pc
nu_acodec_ops_t pNuACodecOps = RT_NULL;
RT_ASSERT(psNuI2s->AcodecOps != RT_NULL);
pNuACodecOps = psNuI2s->AcodecOps;
rt_uint32_t real_samplerate;
/* Open I2S */
if (nu_i2s_capacity_check(pconfig) == RT_TRUE)
......@@ -205,22 +189,108 @@ static rt_err_t nu_i2s_dai_setup(nu_i2s_t psNuI2s, struct rt_audio_configure *pc
if (!pNuACodecOps->nu_acodec_dsp_control || result != RT_EOK)
goto exit_nu_i2s_dai_setup;
real_samplerate = I2S_Open(psNuI2s->i2s_base,
(psNuI2s->AcodecOps->role == NU_ACODEC_ROLE_MASTER) ? I2S_MODE_SLAVE : I2S_MODE_MASTER,
pconfig->samplerate,
(((pconfig->samplebits / 8) - 1) << I2S_CTL0_DATWIDTH_Pos),
(pconfig->channels == 1) ? I2S_ENABLE_MONO : I2S_DISABLE_MONO,
I2S_FORMAT_I2S);
LOG_I("Open I2S.");
/* Open I2S */
if (i2sOpen() != 0)
goto exit_nu_i2s_dai_setup;
/* Select I2S function */
i2sIoctl(I2S_SELECT_BLOCK, I2S_BLOCK_I2S, 0);
/* Open I2S0 interface and set to slave mode, stereo channel, I2S format */
if (pconfig->samplerate != real_samplerate)
/* Select Data width */
i2sIoctl(I2S_SELECT_BIT, ((pconfig->samplebits / 8) - 1), 0);
if (pconfig->channels > 1)
{
LOG_W("Real sample rate: %d Hz != preferred sample rate: %d Hz\n", real_samplerate, pconfig->samplerate);
/* Set to stereo */
i2sIoctl(I2S_SET_CHANNEL, I2S_PLAY, I2S_CHANNEL_P_I2S_TWO);
i2sIoctl(I2S_SET_CHANNEL, I2S_REC, I2S_CHANNEL_R_I2S_TWO);
}
else
{
/* Set to mono */
i2sIoctl(I2S_SET_CHANNEL, I2S_PLAY, I2S_CHANNEL_P_I2S_ONE);
i2sIoctl(I2S_SET_CHANNEL, I2S_REC, I2S_CHANNEL_R_I2S_LEFT_PCM_SLOT0);
}
/* Set DMA interrupt selection to half of DMA buffer */
switch (NU_I2S_DMA_BUF_BLOCK_NUMBER)
{
case 2:
i2sIoctl(I2S_SET_PLAY_DMA_INT_SEL, I2S_DMA_INT_HALF, 0);
i2sIoctl(I2S_SET_REC_DMA_INT_SEL, I2S_DMA_INT_HALF, 0);
break;
case 4:
i2sIoctl(I2S_SET_PLAY_DMA_INT_SEL, I2S_DMA_INT_QUARTER, 0);
i2sIoctl(I2S_SET_REC_DMA_INT_SEL, I2S_DMA_INT_QUARTER, 0);
break;
case 8:
i2sIoctl(I2S_SET_PLAY_DMA_INT_SEL, I2S_DMA_INT_EIGHTH, 0);
i2sIoctl(I2S_SET_REC_DMA_INT_SEL, I2S_DMA_INT_EIGHTH, 0);
break;
default:
RT_ASSERT(0);
break;
}
/* Set DMA buffer address */
i2sIoctl(I2S_SET_DMA_ADDRESS, I2S_PLAY, (uint32_t)&psNuI2s->i2s_dais[NU_I2S_DAI_PLAYBACK].fifo[0]);
i2sIoctl(I2S_SET_DMA_ADDRESS, I2S_REC, (uint32_t)&psNuI2s->i2s_dais[NU_I2S_DAI_CAPTURE].fifo[0]);
/* Set DMA buffer length */
i2sIoctl(I2S_SET_DMA_LENGTH, I2S_PLAY, NU_I2S_DMA_FIFO_SIZE);
i2sIoctl(I2S_SET_DMA_LENGTH, I2S_REC, NU_I2S_DMA_FIFO_SIZE);
/* Set MCLK and enable MCLK */
I2S_EnableMCLK(psNuI2s->i2s_base, __HXT);
/* Select I2S format */
i2sIoctl(I2S_SET_I2S_FORMAT, I2S_FORMAT_I2S, 0);
if (psNuI2s->AcodecOps->role == NU_ACODEC_ROLE_MASTER)
{
if (pconfig->samplerate % 11025)
{
// 12.288MHz ==> APLL=98.4MHz / 8 = 12.3MHz
// APLL is 98.4MHz
/*
FB_DV = 0x28 -> N=FB_DV+1 -> N=41
IN_DV = 0 -> M=IN_DV+1 -> M=1
OUT_DV = 4 -> P=4+1 -> P=5
Fpllout = 12MHz * N / (M*P) -> Fpllout = 12MHz * 41 / (5*1) = 98.4 MHz
*/
outpw(REG_CLK_APLLCON, 0xC0008028);
// Select APLL as I2S source and divider is (7+1)
outpw(REG_CLK_DIVCTL1, (inpw(REG_CLK_DIVCTL1) & ~0x001f0000) | (0x2 << 19) | (0x7 << 24));
// Set sampleing rate, data width, channel
i2sSetSampleRate(12300000, pconfig->samplerate, pconfig->samplebits, pconfig->channels);
}
else
{
// 11.2896MHz ==> APLL=90MHz / 8 = 11.25MHz
// APLL is 90MHz
/*
FB_DV = 0x2D -> N=FB_DV+1 -> N=45
IN_DV = 0 -> M=IN_DV+1 -> M=1
OUT_DV = 5 -> P=5+1 -> P=6
Fpllout = 12MHz * N / (M*P) -> Fpllout = 12MHz * 45 / (6*1) = 90 MHz
*/
outpw(REG_CLK_APLLCON, 0xC000A02D);
// Select APLL as I2S source and divider is (7+1)
outpw(REG_CLK_DIVCTL1, (inpw(REG_CLK_DIVCTL1) & ~0x001f0000) | (0x2 << 19) | (0x7 << 24));
// Set sampleing rate, data width, channel
i2sSetSampleRate(11250000, pconfig->samplerate, pconfig->samplebits, pconfig->channels);
}
// Set as master
i2sIoctl(I2S_SET_MODE, I2S_MODE_MASTER, 0);
}
else
{
// Set as slave, source clock is XIN (12MHz)
i2sIoctl(I2S_SET_MODE, I2S_MODE_SLAVE, 0);
}
LOG_I("Open I2S.");
/* Set unmute */
if (pNuACodecOps->nu_acodec_mixer_control)
......@@ -405,8 +475,14 @@ static rt_err_t nu_i2s_init(struct rt_audio_device *audio)
psNuI2s = (nu_i2s_t)audio;
/* Reset this module */
SYS_ResetModule(psNuI2s->i2s_rst);
/* Enable IP engine clock */
nu_sys_ipclk_enable(psNuI2s->clkidx);
/* Reset IP engine */
nu_sys_ip_reset(psNuI2s->rstidx);
/* Enable interrupt */
rt_hw_interrupt_umask(psNuI2s->irqn);
return -(result);
}
......@@ -428,13 +504,7 @@ static rt_err_t nu_i2s_start(struct rt_audio_device *audio, int stream)
{
case AUDIO_STREAM_REPLAY:
{
nu_i2s_pdma_sc_config(psNuI2s, NU_I2S_DAI_PLAYBACK);
/* Start TX DMA */
I2S_ENABLE_TXDMA(psNuI2s->i2s_base);
/* Enable I2S Tx function */
I2S_ENABLE_TX(psNuI2s->i2s_base);
i2sIoctl(I2S_SET_PLAY, I2S_START_PLAY, 0);
LOG_I("Start replay.");
}
......@@ -442,13 +512,7 @@ static rt_err_t nu_i2s_start(struct rt_audio_device *audio, int stream)
case AUDIO_STREAM_RECORD:
{
nu_i2s_pdma_sc_config(psNuI2s, NU_I2S_DAI_CAPTURE);
/* Start RX DMA */
I2S_ENABLE_RXDMA(psNuI2s->i2s_base);
/* Enable I2S Rx function */
I2S_ENABLE_RX(psNuI2s->i2s_base);
i2sIoctl(I2S_SET_RECORD, I2S_START_REC, 0);
LOG_I("Start record.");
}
......@@ -475,9 +539,7 @@ static rt_err_t nu_i2s_stop(struct rt_audio_device *audio, int stream)
case AUDIO_STREAM_REPLAY:
psNuI2sDai = &psNuI2s->i2s_dais[NU_I2S_DAI_PLAYBACK];
// Disable TX
I2S_DISABLE_TXDMA(psNuI2s->i2s_base);
I2S_DISABLE_TX(psNuI2s->i2s_base);
i2sIoctl(I2S_SET_PLAY, I2S_STOP_PLAY, 0);
LOG_I("Stop replay.");
break;
......@@ -485,9 +547,7 @@ static rt_err_t nu_i2s_stop(struct rt_audio_device *audio, int stream)
case AUDIO_STREAM_RECORD:
psNuI2sDai = &psNuI2s->i2s_dais[NU_I2S_DAI_CAPTURE];
// Disable RX
I2S_DISABLE_RXDMA(psNuI2s->i2s_base);
I2S_DISABLE_RX(psNuI2s->i2s_base);
i2sIoctl(I2S_SET_RECORD, I2S_STOP_REC, 0);
LOG_I("Stop record.");
break;
......@@ -496,20 +556,15 @@ static rt_err_t nu_i2s_stop(struct rt_audio_device *audio, int stream)
return -RT_EINVAL;
}
/* Stop DMA transfer. */
nu_pdma_channel_terminate(psNuI2sDai->pdma_chanid);
/* Close I2S */
if (!(psNuI2s->i2s_base->CTL0 & (I2S_CTL0_TXEN_Msk | I2S_CTL0_RXEN_Msk)))
/* Close I2S if record and playback path. */
if (!((inpw(REG_I2S_RESET)&I2S_RESET_PLAY_Msk) || (inpw(REG_I2S_RESET)&I2S_RESET_RECORD_Msk)))
{
I2S_DisableMCLK(psNuI2s->i2s_base);
I2S_Close(psNuI2s->i2s_base);
i2sClose();
LOG_I("Close I2S.");
}
/* Silence */
rt_memset((void *)psNuI2sDai->fifo, 0, NU_I2S_DMA_FIFO_SIZE);
psNuI2sDai->fifo_block_idx = 0;
return RT_EOK;
}
......@@ -523,7 +578,8 @@ static void nu_i2s_buffer_info(struct rt_audio_device *audio, struct rt_audio_bu
psNuI2s = (nu_i2s_t)audio;
info->buffer = (rt_uint8_t *)psNuI2s->i2s_dais[NU_I2S_DAI_PLAYBACK].fifo ;
/* Define it a NONCACHEABLE address. */
info->buffer = (rt_uint8_t *)((uint32_t)psNuI2s->i2s_dais[NU_I2S_DAI_PLAYBACK].fifo | NONCACHEABLE) ;
info->total_size = NU_I2S_DMA_FIFO_SIZE;
info->block_size = NU_I2S_DMA_BUF_BLOCK_SIZE;
info->block_count = NU_I2S_DMA_BUF_BLOCK_NUMBER;
......@@ -543,21 +599,6 @@ static struct rt_audio_ops nu_i2s_audio_ops =
.buffer_info = nu_i2s_buffer_info
};
static rt_err_t nu_hw_i2s_pdma_allocate(nu_i2s_dai_t psNuI2sDai)
{
/* Allocate I2S nu_dma channel */
if ((psNuI2sDai->pdma_chanid = nu_pdma_channel_allocate(psNuI2sDai->pdma_perp)) < 0)
{
goto nu_hw_i2s_pdma_allocate;
}
return RT_EOK;
nu_hw_i2s_pdma_allocate:
return -(RT_ERROR);
}
int rt_hw_i2s_init(void)
{
int i = 0;
......@@ -565,17 +606,13 @@ int rt_hw_i2s_init(void)
for (i = 0; i < NU_I2S_DAI_CNT; i++)
{
uint8_t *pu8ptr = rt_malloc(NU_I2S_DMA_FIFO_SIZE);
psNuI2sDai = &g_nu_i2s_dev.i2s_dais[i];
psNuI2sDai->fifo = pu8ptr;
rt_memset(pu8ptr, 0, NU_I2S_DMA_FIFO_SIZE);
RT_ASSERT(psNuI2sDai->fifo != RT_NULL);
psNuI2sDai->pdma_chanid = -1;
psNuI2sDai->fifo_block_idx = 0;
RT_ASSERT(nu_hw_i2s_pdma_allocate(psNuI2sDai) == RT_EOK);
/* Allocate playback and record FIFO buffer. */
psNuI2sDai->fifo = (uint8_t *)rt_malloc_align(NU_I2S_DMA_FIFO_SIZE, 32);
RT_ASSERT(psNuI2sDai->fifo != RT_NULL);
RT_ASSERT(nu_pdma_sgtbls_allocate(&psNuI2sDai->pdma_descs[0], NU_I2S_DMA_BUF_BLOCK_NUMBER) == RT_EOK);
rt_memset(psNuI2sDai->fifo, 0, NU_I2S_DMA_FIFO_SIZE);
}
/* Register ops of audio device */
......@@ -584,6 +621,9 @@ int rt_hw_i2s_init(void)
/* Register device, RW: it is with replay and record functions. */
rt_audio_register(&g_nu_i2s_dev.audio, g_nu_i2s_dev.name, RT_DEVICE_FLAG_RDWR, &g_nu_i2s_dev);
/* Register I2S ISR */
rt_hw_interrupt_install(g_nu_i2s_dev.irqn, nu_i2s_isr, &g_nu_i2s_dev, g_nu_i2s_dev.name);
return RT_EOK;
}
INIT_DEVICE_EXPORT(rt_hw_i2s_init);
......
......@@ -6,16 +6,16 @@
*
* Change Logs:
* Date Author Notes
* 2020-2-7 Wayne First version
* 2020-12-12 Wayne First version
*
******************************************************************************/
#ifndef __DRV_I2S_H__
#define __DRV_I2S_H__
#include <rtdevice.h>
#include <rtthread.h>
#include <drv_sys.h>
#include <NuMicro.h>
#include <drv_pdma.h>
#if !defined(NU_I2S_DMA_FIFO_SIZE)
#define NU_I2S_DMA_FIFO_SIZE (2048)
......@@ -34,6 +34,11 @@
#define NU_I2S_DMA_BUF_BLOCK_SIZE (NU_I2S_DMA_FIFO_SIZE/NU_I2S_DMA_BUF_BLOCK_NUMBER)
#if ( NU_I2S_DMA_BUF_BLOCK_SIZE > RT_AUDIO_RECORD_PIPE_SIZE )
#error "Specified I2S DMA buffer size is small than PIPE size in RT driver."
#error "You should enlarge RT_AUDIO_RECORD_PIPE_SIZE. "
#endif
typedef enum
{
NU_I2S_DAI_PLAYBACK,
......@@ -71,11 +76,7 @@ typedef nu_acodec_ops *nu_acodec_ops_t;
struct nu_i2s_dai
{
int16_t pdma_perp;
int8_t pdma_chanid;
rt_uint8_t *fifo;
int16_t fifo_block_idx;
nu_pdma_desc_t pdma_descs[NU_I2S_DMA_BUF_BLOCK_NUMBER];
};
typedef struct nu_i2s_dai *nu_i2s_dai_t;
......@@ -85,8 +86,9 @@ struct nu_i2s
struct rt_audio_configure config;
char *name;
I2S_T *i2s_base;
uint32_t i2s_rst;
IRQn_Type irqn;
E_SYS_IPRST rstidx;
E_SYS_IPCLK clkidx;
struct nu_i2s_dai i2s_dais[NU_I2S_DAI_CNT];
nu_acodec_ops_t AcodecOps;
......
......@@ -18,10 +18,6 @@
#include <NuMicro.h>
#include <board.h>
#ifndef NU_PDMA_SGTBL_POOL_SIZE
#define NU_PDMA_SGTBL_POOL_SIZE (16)
#endif
#define NU_PDMA_CAP_NONE (0 << 0)
#define NU_PDMA_EVENT_ABORT (1 << 0)
......
......@@ -166,7 +166,6 @@ static void SDH_IRQHandler(int vector, void *param)
nu_sdh_t sdh = (nu_sdh_t)param;
SDH_T *sdh_base = sdh->base;
unsigned int volatile isr;
unsigned int volatile ier;
SDH_INFO_T *pSD = sdh->info;
// FMI data abort interrupt
......@@ -325,7 +324,7 @@ static rt_size_t nu_sdh_write(rt_device_t dev, rt_off_t pos, const void *buffer,
RT_ASSERT(dev != RT_NULL);
RT_ASSERT(buffer != RT_NULL);
rt_sem_take(&sdh->lock, RT_WAITING_FOREVER);
result = rt_sem_take(&sdh->lock, RT_WAITING_FOREVER);
RT_ASSERT(result == RT_EOK);
/* Check alignment. */
......@@ -479,9 +478,9 @@ exit_nu_sdh_hotplug_is_mounted:
static rt_err_t nu_sdh_hotplug_mount(nu_sdh_t sdh)
{
rt_err_t ret = RT_ERROR;
DIR *t;
#if defined(RT_USING_DFS)
DIR *t;
if (nu_sdh_hotplug_is_mounted(sdh->mounted_point) == RT_TRUE)
{
......
......@@ -222,14 +222,6 @@ CONFIG_RT_HWCRYPTO_USING_RNG=y
#
# CONFIG_RT_USING_USB_HOST is not set
# CONFIG_RT_USING_USB_DEVICE is not set
# CONFIG__RT_USB_DEVICE_NONE is not set
# CONFIG__RT_USB_DEVICE_CDC is not set
# CONFIG__RT_USB_DEVICE_MSTORAGE is not set
# CONFIG__RT_USB_DEVICE_HID is not set
# CONFIG__RT_USB_DEVICE_RNDIS is not set
# CONFIG__RT_USB_DEVICE_ECM is not set
# CONFIG__RT_USB_DEVICE_WINUSB is not set
# CONFIG__RT_USB_DEVICE_AUDIO is not set
#
# POSIX layer and C standard library
......@@ -692,6 +684,7 @@ CONFIG_NU_PKG_USING_UTILS=y
# CONFIG_NU_PKG_USING_BMX055 is not set
# CONFIG_NU_PKG_USING_MAX31875 is not set
# CONFIG_NU_PKG_USING_NAU88L25 is not set
CONFIG_NU_PKG_USING_NAU8822=y
# CONFIG_NU_PKG_USING_ILI9341 is not set
#
......@@ -705,8 +698,7 @@ CONFIG_SOC_SERIES_NUC980=y
# CONFIG_BSP_USE_STDDRIVER_SOURCE is not set
CONFIG_BSP_USING_MMU=y
CONFIG_BSP_USING_PDMA=y
CONFIG_NU_PDMA_MEMFUN_ACTOR_MAX=4
CONFIG_NU_PDMA_SGTBL_POOL_SIZE=16
CONFIG_NU_PDMA_MEMFUN_ACTOR_MAX=2
CONFIG_BSP_USING_GPIO=y
# CONFIG_BSP_USING_CLK is not set
CONFIG_BSP_USING_EMAC=y
......@@ -716,7 +708,7 @@ CONFIG_BSP_USING_EMAC0=y
CONFIG_BSP_USING_RTC=y
CONFIG_NU_RTC_SUPPORT_IO_RW=y
CONFIG_NU_RTC_SUPPORT_MSH_CMD=y
# CONFIG_BSP_USING_ADC is not set
CONFIG_BSP_USING_ADC=y
CONFIG_BSP_USING_TMR=y
CONFIG_BSP_USING_TIMER=y
CONFIG_BSP_USING_TMR0=y
......@@ -755,7 +747,7 @@ CONFIG_BSP_USING_UART1_RX_DMA=y
# CONFIG_BSP_USING_UART8 is not set
# CONFIG_BSP_USING_UART9 is not set
CONFIG_BSP_USING_I2C=y
# CONFIG_BSP_USING_I2C0 is not set
CONFIG_BSP_USING_I2C0=y
# CONFIG_BSP_USING_I2C1 is not set
CONFIG_BSP_USING_I2C2=y
# CONFIG_BSP_USING_I2C3 is not set
......@@ -776,7 +768,8 @@ CONFIG_BSP_USING_SPI0=y
CONFIG_BSP_USING_SPI0_PDMA=y
CONFIG_BSP_USING_SPI1_NONE=y
# CONFIG_BSP_USING_SPI1 is not set
# CONFIG_BSP_USING_I2S is not set
CONFIG_BSP_USING_I2S=y
CONFIG_NU_I2S_DMA_FIFO_SIZE=4096
CONFIG_BSP_USING_QSPI=y
CONFIG_BSP_USING_QSPI0=y
# CONFIG_BSP_USING_QSPI0_PDMA is not set
......@@ -797,7 +790,7 @@ CONFIG_BSP_USING_WDT=y
#
CONFIG_BSP_USING_CONSOLE=y
CONFIG_BOARD_USING_IP101GR=y
# CONFIG_BOARD_USING_NAU8822L is not set
CONFIG_BOARD_USING_NAU8822=y
CONFIG_BOARD_USING_STORAGE_SDCARD=y
CONFIG_BOARD_USING_STORAGE_SPIFLASH=y
CONFIG_BOARD_USING_USB_NONE=y
......
# NK-980IoT
## 1. Introduction
Nuvoton Technology provides industrial IoT development platform using NUC980DK61Y microprocessor, which is an industrial IoT open platform, including complete hardware design and software reference design. The platform is powered by Nuvoton ARM9 MPU with a 300 MHz internal speed and up to 128 MB DDR memory, single 10/100 Ethernet port, 2 high-speed USB hosts, 1 Gb SPI NAND Flash memory, microphone input, stereo headphone output and Arduino compatible interface. The platform supports embedded Linux OS which provides all IoT protocols you need, users can use this platform to quickly develop a plenty of IoT applications.
Nuvoton Technology provides industrial IoT development platform using NUC980DK61Y microprocessor, which is an industrial IoT open platform, including complete hardware design and software reference design. The platform is powered by Nuvoton ARM9 MPU with a 300 MHz internal speed and up to 128 MB DDR memory, single 10/100 Ethernet port, 2 high-speed USB hosts, 1 Gb SPI NAND Flash memory, microphone input, stereo headphone output and Arduino compatible interface.
[![NK-980IOT](https://i.imgur.com/bKvBJLA.png "NK-980IOT")](https://i.imgur.com/bKvBJLA.png "NK-980IOT")
......@@ -10,7 +10,7 @@ Nuvoton Technology provides industrial IoT development platform using NUC980DK61
| | Features |
| -- | -- |
| Part NO. | NUC980DK61Y |
| MCU ARCH. | 32-bit ARM926EJ-STM |
| CPU ARCH. | 32-bit ARM926EJ-STM |
| Operation frequency | 300 MHz |
| Embedded SDRAM size | Built-in 64MB (Up to 128 MB DDR memory) |
| Crypto engine | AES, RSA, ECC, HMAC and SHA crypto accelerator |
......@@ -41,9 +41,9 @@ Nuvoton Technology provides industrial IoT development platform using NUC980DK61
|Ethernet PHY | IP101GR | Supported |
|Button | | Supported |
|LEDs | | Supported |
|Audio Codec | NAU88L22, Supports MIC and earphone | Not ready |
|SPI NAND flash | W25Q32 | Not ready |
|Virtual COM | For console | Need to install virtual com port driver. |
|Audio Codec | NAU8822, Supports MIC and earphone | Supported |
|SPI NAND flash | W25N01GVZE1G | Not ready |
|Virtual COM | For console | Ready.(Need to install VCOM driver) |
## 2. Supported compiler
Support GCC, MDK4 and MDK5 IDE/compilers. More information of these compiler version as following:
......@@ -66,6 +66,8 @@ Choose type: DDR/SRAM<br>
Choose file: Specify your rtthread.bin file.<br>
Execute Address: 0x0<br>
Option: Download and run<br>
<< Press Download >><br>
Enjoy!! <br>
<br>
### 3.2 SPI NOR flash using NuWriter
......@@ -78,6 +80,10 @@ Choose file: Specify your rtthread.bin file.<br>
Image Type: Loader<br>
Execute Address: 0x0<br>
<< Press Program >><br>
<< Press OK & Wait it down >><br>
<< Set Power-on setting to SPI NOR booting >><br>
<< Press Reset button on board >><br>
Enjoy!! <br>
<br>
## 4. Test
......@@ -89,9 +95,9 @@ You can use Tera Term terminate emulator (or other software) to type commands of
* [Nuvoton Direct][1]
## 6. Resources
* [Download board schematics][2]
* [Download Board Schematics][2]
* [Download NK-980IOT Quick Start Guide][3]
* [Download MCU TRM][4]
* [Download TRM][4]
* [Download NuWriter][5]
[1]: https://direct.nuvoton.com/tw/numaker-iiot-nuc980
......
......@@ -148,8 +148,8 @@ INIT_ENV_EXPORT(filesystem_init);
extern rt_err_t ramdisk_init(const char *dev_name, rt_uint8_t *disk_addr, rt_size_t block_size, rt_size_t num_block);
int ramdisk_device_init(void)
{
/* Create a 16MB RAMDISK */
return (int)ramdisk_init(RAMDISK_NAME, NULL, 512, 2048 * 4);
/* Create a 8MB RAMDISK */
return (int)ramdisk_init(RAMDISK_NAME, NULL, 512, 2 * 8192);
}
INIT_DEVICE_EXPORT(ramdisk_device_init);
#endif
......
......@@ -18,9 +18,9 @@ menu "Hardware Drivers Config"
default n
config BOARD_USING_NAU8822L
bool "NAU8822L Audio Codec supporting(over i2s, i2c2)"
select NU_PKG_USING_NAU8822L
config BOARD_USING_NAU8822
bool "NAU8822 Audio Codec supporting(over i2s, i2c0)"
select NU_PKG_USING_NAU8822
select BSP_USING_I2C0
select BSP_USING_I2S
select BSP_USING_I2S0
......
......@@ -6,7 +6,7 @@
*
* Change Logs:
* Date Author Notes
* 2020-1-16 Wayne First version
* 2020-12-12 Wayne First version
*
******************************************************************************/
......@@ -93,8 +93,6 @@ static void SpiFlash_ExitQspiMode(struct rt_qspi_device *qspi_device)
static int rt_hw_spiflash_init(void)
{
/* Here, we use Dual I/O to drive the SPI flash by default. */
/* If you want to use Quad I/O, you can modify to 4 from 2 and crossover D2/D3 pin of SPI flash. */
if (nu_qspi_bus_attach_device("qspi0", "qspi01", 4, SpiFlash_EnterQspiMode, SpiFlash_ExitQspiMode) != RT_EOK)
return -1;
......@@ -194,25 +192,25 @@ static void at_wifi_set(int argc, char **argv)
#endif /* BOARD_USING_ESP8266 */
#if defined(BOARD_USING_NAU88L25) && defined(NU_PKG_USING_NAU88L25)
#include <acodec_nau88l25.h>
S_NU_NAU88L25_CONFIG sCodecConfig =
#if defined(BOARD_USING_NAU8822) && defined(NU_PKG_USING_NAU8822)
#include <acodec_nau8822.h>
S_NU_NAU8822_CONFIG sCodecConfig =
{
.i2c_bus_name = "i2c0",
.i2s_bus_name = "sound0",
.pin_phonejack_en = NU_GET_PININDEX(NU_PE, 13),
.pin_phonejack_en = 0,
.pin_phonejack_det = 0,
};
int rt_hw_nau88l25_port(void)
int rt_hw_nau8822_port(void)
{
if (nu_hw_nau88l25_init(&sCodecConfig) != RT_EOK)
if (nu_hw_nau8822_init(&sCodecConfig) != RT_EOK)
return -1;
return 0;
}
INIT_COMPONENT_EXPORT(rt_hw_nau88l25_port);
#endif /* BOARD_USING_NAU88L25 */
INIT_COMPONENT_EXPORT(rt_hw_nau8822_port);
#endif /* BOARD_USING_NAU8822 */
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册