在线时间588 小时
UID3469866
注册时间2018-4-19
NXP金币2995
TA的每日心情 | 慵懒 2024-2-8 09:39 |
---|
签到天数: 217 天 [LV.7]常住居民III
版主
- 积分
- 7637
- 最后登录
- 2024-4-25
|
今天我们来聊一聊RT1052串口的使用方式,还是之前的程序。既然要自己配置串口,那么肯定要先和官方给的配置说再见。
首先注释掉
//#include "fsl_debug_console.h"
//BOARD_InitDebugConsole();
//PRINTF("hello world.\r\n");
这三句。然后添加一个头文件
#include "fsl_lpuart.h"
说到初始化,首先想到的就应该是init了吧,先看一下fsl_lpuart.h"里面有关的定义。
status_t LPUART_Init(LPUART_Type *base, const lpuart_config_t *config, uint32_t srcClock_Hz);
可以看到,一共要输入三个参数,第一个是配置哪一个串口,第二个是一个lpuart_config_t*类型的量(里面是串口配置的一些信息),第三个是LPUART时钟的主频。
先来看一下lpuart_config_t,在fsl_lpuart.h"可以找到定义
/*! @brief LPUART configuration structure. */
typedef struct _lpuart_config
{
uint32_t baudRate_Bps; /*!< LPUART baud rate */
lpuart_parity_mode_t parityMode; /*!< Parity mode, disabled (default), even, odd */
lpuart_data_bits_t dataBitsCount; /*!< Data bits count, eight (default), seven */
bool isMsb; /*!< Data bits order, LSB (default), MSB */
#if defined(FSL_FEATURE_LPUART_HAS_STOP_BIT_CONFIG_SUPPORT) && FSL_FEATURE_LPUART_HAS_STOP_BIT_CONFIG_SUPPORT
lpuart_stop_bit_count_t stopBitCount; /*!< Number of stop bits, 1 stop bit (default) or 2 stop bits */
#endif
#if defined(FSL_FEATURE_LPUART_HAS_FIFO) && FSL_FEATURE_LPUART_HAS_FIFO
uint8_t txFifoWatermark; /*!< TX FIFO watermark */
uint8_t rxFifoWatermark; /*!< RX FIFO watermark */
#endif
#if defined(FSL_FEATURE_LPUART_HAS_MODEM_SUPPORT) && FSL_FEATURE_LPUART_HAS_MODEM_SUPPORT
bool enableRxRTS; /*!< RX RTS enable */
bool enableTxCTS; /*!< TX CTS enable */
lpuart_transmit_cts_source_t txCtsSource; /*!< TX CTS source */
lpuart_transmit_cts_config_t txCtsConfig; /*!< TX CTS configure */
#endif
lpuart_idle_type_select_t rxIdleType; /*!< RX IDLE type. */
lpuart_idle_config_t rxIdleConfig; /*!< RX IDLE configuration. */
bool enableTx; /*!< Enable TX */
bool enableRx; /*!< Enable RX */
} lpuart_config_t;
可以看到这里有很多的#if #endif (其他程序也有大量的这个)。这个设计我感觉是一个比较好的设计。这些的定义都在MIMXRT1052_features.h这个头文件里面找到,下图是部分配置
可以看到,可以利用这里面的宏定义对程序进行裁剪,去掉不需要的功能(让它不参与编译)。这样就能提高程序效率(很好的设计)。
然后模仿它去写一个配置。
lpuart_config_t lpuart1_config;
lpuart1_config.baudRate_Bps = 115200;
lpuart1_config.parityMode = kLPUART_ParityDisabled; //奇偶校验无
lpuart1_config.dataBitsCount = kLPUART_EightDataBits; //8位数据
lpuart1_config.isMsb = false; //LSB
#if defined(FSL_FEATURE_LPUART_HAS_STOP_BIT_CONFIG_SUPPORT) && FSL_FEATURE_LPUART_HAS_STOP_BIT_CONFIG_SUPPORT
lpuart1_config.stopBitCount = kLPUART_OneStopBit; //1个停止位
#endif
#if defined(FSL_FEATURE_LPUART_HAS_FIFO) && FSL_FEATURE_LPUART_HAS_FIFO
lpuart1_config.txFifoWatermark = 0; //TX缓存区大小
lpuart1_config.rxFifoWatermark = 0; //RX缓存大小
#endif
#if defined(FSL_FEATURE_LPUART_HAS_MODEM_SUPPORT) && FSL_FEATURE_LPUART_HAS_MODEM_SUPPORT
lpuart1_config.enableRxRTS = false; //不使用RTS
lpuart1_config.enableTxCTS = false; //不使用CTS
lpuart1_config.txCtsSource = kLPUART_CtsSourcePin; //CTS信号的来源是CTS脚
lpuart1_config.txCtsConfig = kLPUART_CtsSampleAtStart; //CTS输入在每个字符的起始处被采样
#endif
lpuart1_config.rxIdleType = kLPUART_IdleTypeStopBit; //停止位之后开始计算多少字节之后进入空闲
lpuart1_config.rxIdleConfig = kLPUART_IdleCharacter1; //1个空闲字节进入空闲
lpuart1_config.enableTx = true; //使能TX
lpuart1_config.enableRx = true; //使能RX
这样感觉有点麻烦,不过没问题,官方提供了一个简单的快速配置
可以通过调用它来实现串口的快速配置
LPUART_GetDefaultConfig(&config);
config.enableTx = true; //使能TX
config.enableRx = true; //使能RX
这样也能实现配置的效果。
然后初始化还有一个很关键的要素 LPUART时钟的主频(这里不讨论外设时钟的配置方式,外设时钟的配置方式见(飞凌RT1052——6.PIT中断与外设时钟配置https://www.nxpic.org.cn/module/foru ... 189&fromuid=3469866(出处: 恩智浦技术社区))。
可以看到LPUART时钟的来源有两个 一个是OSC,一个是pll3_sw_clk的6分频,由CSCDR1[UART_CLK_SEL]位选择,然后经过CSCDR1[UART_CLK_PODF]位进行分频,这里直接给出有关寄存器的图
上图可以看到pll3_sw_clk的来源,正是PLL3
我们这次不讨论时钟的配置方式。直接看官方提供给我们的一个时钟获取函数
uint32_t BOARD_DebugConsoleSrcFreq(void)
{
uint32_t freq;
/* To make it simple, we assume default PLL and divider settings, and the only variable
from application is use PLL3 source or OSC source */
if (CLOCK_GetMux(kCLOCK_UartMux) == 0) /* PLL3 div6 80M */
{
freq = (CLOCK_GetPllFreq(kCLOCK_PllUsb1) / 6U) / (CLOCK_GetDiv(kCLOCK_UartDiv) + 1U);
}
else
{
freq = CLOCK_GetOscFreq() / (CLOCK_GetDiv(kCLOCK_UartDiv) + 1U);
}
return freq;
}
先看一下kCLOCK_UartMux的定义
kCLOCK_UartMux = CCM_TUPLE(CSCDR1, CCM_CSCDR1_UART_CLK_SEL_SHIFT, CCM_CSCDR1_UART_CLK_SEL_MASK, CCM_NO_BUSY_WAIT),
很明显是用来操作UART_CLK_SEL位了,这里CLOCK_GetMux(kCLOCK_UartMux) 函数就是为了获取UART_CLK_SEL位状态判断时钟来源
然后是kCLOCK_PllUsb1
kCLOCK_PllUsb1 = CCM_ANALOG_TUPLE(PLL_USB1, CCM_ANALOG_PLL_USB1_ENABLE_SHIFT),
这里如果按之前分析的话,应该是获取PLL3的主频的,可这PLL_USB1是个啥
不过可以在参考手册中找到,确实是PLL3的别称。CLOCK_GetPllFreq(kCLOCK_PllUsb1) 就是为了获取pll3_sw_clk的频率。
CLOCK_GetOscFreq() 是为了得到OSC频率(不得不说这名起的很好理解)
最后是kCLOCK_UartDiv
kCLOCK_UartDiv = CCM_TUPLE(CSCDR1, CCM_CSCDR1_UART_CLK_PODF_SHIFT, CCM_CSCDR1_UART_CLK_PODF_MASK, CCM_NO_BUSY_WAIT),
很明显CLOCK_GetDiv(kCLOCK_UartDiv) 可以获取UART_CLK_PODF的取值。
而
freq = (CLOCK_GetPllFreq(kCLOCK_PllUsb1) / 6U) / (CLOCK_GetDiv(kCLOCK_UartDiv) + 1U);
freq = CLOCK_GetOscFreq() / (CLOCK_GetDiv(kCLOCK_UartDiv) + 1U);
正是一开始观察时钟图可以的到的式子(分频可参考参考手册)
最终就可以用
LPUART_Init(LPUART1, &lpuart1_config, BOARD_DebugConsoleSrcFreq()); //初始化串口1
配置完串口,接下来是处理串口接收中断,下面给出配置
LPUART_EnableInterrupts(LPUART1, kLPUART_RxDataRegFullInterruptEnable); //设置串口1接收到数据满了触发中断
EnableIRQ(LPUART1_IRQn);//使能串口1中断
EnableIRQ(LPUART1_IRQn)没啥好说的,就是使能中断
然后LPUART_EnableInterrupts有两个参数,第一个是选择串口几,第二个就是选择中断触发源
enum _lpuart_interrupt_enable
{
#if defined(FSL_FEATURE_LPUART_HAS_LIN_BREAK_DETECT) && FSL_FEATURE_LPUART_HAS_LIN_BREAK_DETECT
kLPUART_LinBreakInterruptEnable = (LPUART_BAUD_LBKDIE_MASK >> 8), /*!< LIN break detect. */
#endif
kLPUART_RxActiveEdgeInterruptEnable = (LPUART_BAUD_RXEDGIE_MASK >> 8), /*!< Receive Active Edge. */
kLPUART_TxDataRegEmptyInterruptEnable = (LPUART_CTRL_TIE_MASK), /*!< Transmit data register empty. */
kLPUART_TransmissionCompleteInterruptEnable = (LPUART_CTRL_TCIE_MASK), /*!< Transmission complete. */
kLPUART_RxDataRegFullInterruptEnable = (LPUART_CTRL_RIE_MASK), /*!< Receiver data register full. */
kLPUART_IdleLineInterruptEnable = (LPUART_CTRL_ILIE_MASK), /*!< Idle line. */
kLPUART_RxOverrunInterruptEnable = (LPUART_CTRL_ORIE_MASK), /*!< Receiver Overrun. */
kLPUART_NoiseErrorInterruptEnable = (LPUART_CTRL_NEIE_MASK), /*!< Noise error flag. */
kLPUART_FramingErrorInterruptEnable = (LPUART_CTRL_FEIE_MASK), /*!< Framing error flag. */
kLPUART_ParityErrorInterruptEnable = (LPUART_CTRL_PEIE_MASK), /*!< Parity error flag. */
#if defined(FSL_FEATURE_LPUART_HAS_FIFO) && FSL_FEATURE_LPUART_HAS_FIFO
kLPUART_TxFifoOverflowInterruptEnable = (LPUART_FIFO_TXOFE_MASK >> 8), /*!< Transmit FIFO Overflow. */
kLPUART_RxFifoUnderflowInterruptEnable = (LPUART_FIFO_RXUFE_MASK >> 8), /*!< Receive FIFO Underflow. */
#endif
};
官方又给了一大堆触发方式,这里选择kLPUART_RxDataRegFullInterruptEnable,就是接收到数据满了触发中断(这里可以根据自己实际需要来配置)而这个满了的概念就是看接收的数据的数量是否大于接收缓存(这里设置为0)
下面是中断回调函数
void LPUART1_IRQHandler(void)
{
uint8_t data;
if ((kLPUART_RxDataRegFullFlag) & LPUART_GetStatusFlags(LPUART1)) //判断是不是接收到数据触发中断
{
data = LPUART_ReadByte(LPUART1);//读取数据接收区字符
LPUART_WriteByte(LPUART1, data);//向发送区写入一个字符
}
__DSB();
}
RT1052的串口中断有真么多的触发方式,那么我们就要先判断是不是我们需要的触发方式,利用(kLPUART_RxDataRegFullFlag) & LPUART_GetStatusFlags(LPUART1)就可以判断中断触发方式。至于为啥没清中断标志位,这里可以看
只要读取了LPUART_DATA 就可以清空标志位,这里面放的正是接收到的数据。
这里使用LPUART_ReadByte和LPUART_WriteByte构成一个回显函数。
最后主程序还有一句
LPUART_WriteBlocking(LPUART1, "你好世界\n",sizeof("你好世界\n"));//阻塞方式发送一串字符
三个变量,发送的串口号,发送字符的地址,发送字符的数量,这里sizeof本是获取所占字节数的,但char类型正好就是1个字节,所以直接这样用了。
实验效果,打印 你好世界 之后回显
话说配置时有时遇到一个小bug,就是有时候下完程序一部分不能正常运行,要手动按下复位键才可以(猜想和软复位有关,有时间再讨论)
最后附上程序
MDK版本:5.24
pack:NXP.MIMXRT1052_DFP.10.0.1.pack
下载算法:飞凌嵌入式提供的 MIMXRT_QSPIFLASH.FLM
启动方式:spi flash启动
下载方式:STlink
3.uart.zip
(1.46 MB, 下载次数: 123)
|
|