【RTD MCAL 篇2】 K312 UART DMA
- 一,文档简介
- 二, 功能实现
- 2.1 K312 MINIEVB硬件配置
- 2.2 EB 配置
- 2.2.1 Mcl module
- 2.2.2 Mcu module
- 2.2.3 Platform module
- 2.2.4 Port module
- 2.2.5 Uart module
- 2.2.6 Rm module
- 2.3 main code
- 三,测试结果
一,文档简介
十一假期时间余额已经不足了,抓紧时间赶一篇MCAL的K312 UART DMA。本篇文章讲解同样是基于S32DS+EB配置,RTD400, 以后其他模块的MCAL锻炼都会基于这样的结构,不过,本文会提供一个命令行版本的代码。如果需要命令行模式,完全可以直接在RTD MCAL的代码包下拷贝一个,并且利用VScode去编译。本篇文章硬件基于K312-miniEVB,板子情况是这样的:
功能:在K312 MCAL代码中,使用DMA方式实现UART的收发功能。
由于RTD400不带有K312的例程,这里还涉及到一个从RTD400移植到K312 MCAL的过程,当然前面的文章已经讲的非常清楚,并且还给出了S32DS工程模版。本文将基于之前的S32DS EB工程模版去做。
二, 功能实现
2.1 K312 MINIEVB硬件配置
谈到硬件配置,由于本文只是使用UART,所以结构非常简单,用到引脚:
LPUART3_TX : PTD2
LPUART3_RX : PTD3
并且外接TTL-USB工具实现信号通讯。
2.2 EB 配置
这里罗列出所有本文相关的EB tresos里面使用到的模块,并且重点给予需要具体配置的模块讲解。
2.2.1 Mcl module
需要配置Dma Logic Channel界面,主要目的是为LPUART3_TX 和RX配置两个DMA通道。
(1)dmalogicChannel_Type_0
(2)dmalogicChannel_Type_2
这里面注册的callback也是可以直接在代码里面去调用的。
2.2.2 Mcu module
Mcu->McuClockSettingConfig->McuClockReferencePoint->Lpuart3_clk
实际上就是配置LPUART的时钟源频率为24Mhz,来源于AIPS_SLOW_CLK。
2.2.3 Platform module
Platform->Interrupt Controller->IntCtrlConfig,配置3个通道:
这里只需要关注LPUART3中断,以及DMA0 通道6, 通道7中断,因为这两个DMA通道是配置给UART的TX,RX的。FlexIO忽略,只是原始例程里面有没有删掉而已。
2.2.4 Port module
Port->PortContainer, 添加PTD2,PTD3引脚:
2.2.5 Uart module
有两个地方需要配置:
(1)uart->General
(2)uart->uartChannel
这里有4个点要注意:
点1:选择mcu里面配置的时钟源
点2:配置波特率为115200
点3:选择异步方式为DMA
点4:选择mcl里面配置的两个DMA通道,需要把TX和RX匹配到对应的DMA通道。
2.2.6 Rm module
Rm->DMA MUX
配置两个DMA_MUX通道。
2.3 main code
#include "Mcl.h"
#include "Mcu.h"
#include "CDD_Uart.h"
#include "CDD_Rm.h"
#include "Port.h"
#include "Platform.h"
#include "Lpuart_Uart_Ip_Irq.h"
#include "Flexio_Uart_Ip_Irq.h"
//#include "check_example.h"
#include <string.h>
#include "Port_Cfg.h"
#define UART_LPUART_INTERNAL_CHANNEL 0U
#define UART_FLEXIO_TX_CHANNEL 1U
#define UART_FLEXIO_RX_CHANNEL 2U
/* Welcome messages displayed at the console */
#define WELCOME_MSG "MCAL UART DMA Helloworld for automotive with S32K312!\r\n"
/* Error message displayed at the console, in case data is received erroneously */
#define ERROR_MSG "An error occurred! The application will stop!\r\n"
/* Length of the message to be received from the console */
#define MSG_LEN 50U
#define UART_BUFFER_LENGTH ((uint32)10U)
Std_ReturnType T_Uart_Status;
//uint8 Rx_Buffer[UART_BUFFER_LENGTH];
#define UART_START_SEC_VAR_CLEARED_UNSPECIFIED_NO_CACHEABLE
#include "Uart_Memmap.h"
__attribute__(( aligned(32) )) uint8 Rx_Buffer[UART_BUFFER_LENGTH];
#define UART_STOP_SEC_VAR_CLEARED_UNSPECIFIED_NO_CACHEABLE
#include "Uart_Memmap.h"
uint32 g_Uart_CallbackCounter = 0U;
uint32 g_DmaCh16_ErrorCallbackCounter = 0U;
uint32 g_DmaCh17_ErrorCallbackCounter = 0U;
//void Uart_Callback (void);
void Uart_Callback(const uint8 HwInstance, const Lpuart_Uart_Ip_EventType Event, void *UserData);
void Mcl_DmaCh16_ErrorCallback (void);
void Mcl_DmaCh17_ErrorCallback (void);
void Uart_Callback(const uint8 HwInstance, const Lpuart_Uart_Ip_EventType Event, void *UserData)
{
if(Event == LPUART_UART_IP_EVENT_END_TRANSFER)
{
__asm volatile ("nop");
__asm volatile ("nop");
__asm volatile ("nop");
__asm volatile ("nop");
__asm volatile ("nop");
__asm volatile ("nop");
}
else if (Event == LPUART_UART_IP_EVENT_TX_EMPTY)
{
__asm volatile ("nop");
__asm volatile ("nop");
}
else if (Event == LPUART_UART_IP_EVENT_RX_FULL)
{
__asm volatile ("nop");
}
else if (Event == LPUART_UART_IP_EVENT_ERROR)
{
__asm volatile ("nop");
}
else
{
__asm volatile ("nop");
}
}
void Mcl_DmaCh6_ErrorCallback (void)
{
g_DmaCh16_ErrorCallbackCounter++;
}
void Mcl_DmaCh7_ErrorCallback (void)
{
g_DmaCh17_ErrorCallbackCounter++;
}
boolean User_Str_Cmp(const uint8 * pBuffer1, const uint8 * pBuffer2, const uint32 length)
{
uint32 idx = 0;
for (idx = 0; idx < length; idx++)
{
if(pBuffer1[idx] != pBuffer2[idx])
{
return FALSE;
}
}
return TRUE;
}
/**
* @brief Main function of the example
* @details Initializez the used drivers and uses the Icu
* and Dio drivers to toggle a LED on a push button
*/
int main(void)
{
Std_ReturnType UartStatus = E_NOT_OK;
uint32 RemainingBytes;
uint32 Timeout = 0xFFFFFF;
Uart_StatusType UartReceiveStatus = UART_STATUS_TIMEOUT;
Uart_StatusType UartTransmitStatus = UART_STATUS_TIMEOUT;
/* Initialize the Mcu driver */
Mcu_Init(NULL_PTR);
Mcu_InitClock(McuClockSettingConfig_0);
Mcu_SetMode(McuModeSettingConf_0);
/* Initialize Mcl module */
Mcl_Init(NULL_PTR);
/* Initialize Rm driver for using DmaMux*/
Rm_Init (NULL_PTR);
/* Initialize all pins using the Port driver */
Port_Init(NULL_PTR);
/* Initialize IRQs */
Platform_Init(NULL_PTR);
/* Initializes an UART driver*/
Uart_Init(NULL_PTR);
T_Uart_Status = Uart_AsyncSend(UART_LPUART_INTERNAL_CHANNEL, (const uint8 *)WELCOME_MSG, strlen(WELCOME_MSG));
if (E_OK == T_Uart_Status)
{
do
{
/* Get transmission status */
UartTransmitStatus = Uart_GetStatus (UART_LPUART_INTERNAL_CHANNEL, &RemainingBytes, UART_SEND);
}
while (UART_STATUS_NO_ERROR != UartTransmitStatus && 0 < Timeout--);
Timeout = 0xFFFFFF;
UartTransmitStatus = UART_STATUS_TIMEOUT;
}
for(;;)
{
/* Receive data from the PC - Get 10 bytes in total */
UartStatus = Uart_AsyncReceive (UART_LPUART_INTERNAL_CHANNEL, Rx_Buffer, UART_BUFFER_LENGTH);
if (E_OK == UartStatus)
{
do
{
/* Get receive status */
UartReceiveStatus = Uart_GetStatus (UART_LPUART_INTERNAL_CHANNEL, &RemainingBytes, UART_RECEIVE);
}
while (UART_STATUS_NO_ERROR != UartReceiveStatus && 0 < Timeout--);
Timeout = 0xFFFFFF;
UartReceiveStatus = UART_STATUS_TIMEOUT;
}
UartStatus = E_NOT_OK;
/* Send data to the PC - Echo back the received data */
UartStatus = Uart_AsyncSend (UART_LPUART_INTERNAL_CHANNEL, Rx_Buffer, UART_BUFFER_LENGTH);
if (E_OK == UartStatus)
{
do
{
/* Get transmission status */
UartTransmitStatus = Uart_GetStatus (UART_LPUART_INTERNAL_CHANNEL, &RemainingBytes, UART_SEND);
}
while (UART_STATUS_NO_ERROR != UartTransmitStatus && 0 < Timeout--);
Timeout = 0xFFFFFF;
UartTransmitStatus = UART_STATUS_TIMEOUT;
}
UartStatus = E_NOT_OK;
}
Uart_Deinit();
Mcl_DeInit();
// Exit_Example((T_Uart_Status1 == E_OK) && (T_Uart_Status2 == E_OK));
return (0U);
}
这里需要注意的是,根据RTD C:\NXP\SW32K3_S32M27x_RTD_R21-11_4.0.0\eclipse\plugins\Uart_TS_T40D34M40I0R0\doc的RTD_UART_IM.pdf, RTD_UART_UM.pdf.
在做DMA传输的时候,需要把buffer放到noncacheable区域。
所以才有本文的:
#define UART_START_SEC_VAR_CLEARED_UNSPECIFIED_NO_CACHEABLE
#include "Uart_Memmap.h"
__attribute__(( aligned(32) )) uint8 Rx_Buffer[UART_BUFFER_LENGTH];
#define UART_STOP_SEC_VAR_CLEARED_UNSPECIFIED_NO_CACHEABLE
#include "Uart_Memmap.h"
三,测试结果
使用UART3,引脚UART3_TX:PTD2, UART3_RX:PTD3
芯片复位之后,先发送:
Helloworld for automotive with S32K344!
然后等待接受,接收到10个字节的数据之后,产生uart_callback中断,进入到LPUART_UART_IP_ENET_END_TRANSFER里面,你这里打断点会看到,RX_Buffer里面收到的数据和发送的数据一致。
然后,代码会将收到的数据回环出去。测试情况如下:
我下图是测试了两组:
PC发送: 1234567890,MCU收到之后,回环回来。
PC发送:0987654321,MCU收到之后,debug停到断点,可以查看收到的buffer情况,你可以看到,buffer数据已经正确。
附件两个代码包:
(1)Uart_TS_T40D34M40I0R0_miniK312_3.zipEB MCAL 命令行方式
该代码,解压之后放到:C:\NXP\SW32K3_S32M27x_RTD_R21-11_4.0.0\eclipse\plugins,然后就可以直接用命令行去编译。
(2)Mcal_UARTDMA_S32K312_RTD400_S32DS.zip:导入到S32DS的方式,当然里面已经包含了EB的工程:
文章附件代码链接:
https://community.nxp.com/t5/S32K-Knowledge-Base/RTD400-MCAL-K312-UART-DMA/ta-p/1967467