UART(Universal Asynchronous Receiver/Transmitter)是一种通用的串行通信接口,用于实现两个设备之间的异步数据传输。异步通信的特点是数据在发送和接收之间没有固定的时钟同步,而是通过起始位和停止位来标识数据帧的开始和结束。UART广泛应用于嵌入式系统中,如单片机与PC、单片机与传感器、单片机与无线模块之间的通信。
在Microchip SAM L系列单片机中,UART接口由USART(Universal Synchronous/Asynchronous Receiver-Transmitter)模块实现。USART模块支持多种通信模式,包括同步和异步模式,其中异步模式就是我们通常所说的UART。SAM L系列单片机的USART模块具有以下特点:
支持多种波特率设置,满足不同通信需求。
支持数据格式的灵活配置,如数据位、停止位和奇偶校验位。
内置硬件流控制,提高数据传输的可靠性。
支持DMA(Direct Memory Access)传输,减轻CPU负担。
在使用SAM L系列单片机的UART通信接口之前,需要进行一些基本的配置。这些配置包括设置波特率、数据格式、中断和DMA等。以下是一些常见的配置步骤:
波特率是指每秒钟传输的位数,常见的波特率有9600、115200等。在SAM L系列单片机中,波特率的配置通过设置USART模块的波特率寄存器来实现。
// UART配置波特率
void UART_SetBaudRate(SercomUsart *USART, uint32_t baudRate, uint32_t gclk) {
// 计算波特率因子
uint16_t baudFactor = (gclk / (16 * baudRate)) - 1;
// 配置波特率寄存器
USART->BAUD.bit.BAUD = baudFactor;
}
数据格式包括数据位、停止位和奇偶校验位。SAM L系列单片机的USART模块支持多种数据格式配置。
// UART配置数据格式
void UART_SetDataFormat(SercomUsart *USART, uint8_t dataBits, uint8_t stopBits, uint8_t parity) {
// 配置数据位
USART->CTRLA.bit.SAMPR = (dataBits == 9) ? 1 : 0;
USART->CTRLB.bit.CHARSIZE = (dataBits == 8) ? 0 : (dataBits == 9) ? 1 : 2;
// 配置停止位
USART->CTRLB.bit.STOP = (stopBits == 1) ? 0 : 1;
// 配置奇偶校验位
USART->CTRLB.bit.PMODE = (parity == UART_PARITY_NONE) ? 0 : 1;
USART->CTRLB.bit.PAR = parity;
}
UART通信接口可以通过中断来处理数据的发送和接收,减轻CPU的负担。在SAM L系列单片机中,可以通过配置USART模块的中断寄存器来启用中断。
// UART配置中断
void UART_SetInterrupt(SercomUsart *USART, uint8_t interruptMask) {
// 配置中断使能寄存器
USART->INTENSET.reg = interruptMask;
}
DMA(Direct Memory Access)传输可以进一步提高数据传输的效率,减轻CPU的负担。在SAM L系列单片机中,可以通过配置DMA控制器来实现UART的DMA传输。
// UART配置DMA传输
void UART_SetDMA(SercomUsart *USART, DmaChannel *txChannel, DmaChannel *rxChannel) {
// 配置DMA传输通道
dma_channel_set_source(txChannel, (uint32_t)&USART->DATA.reg);
dma_channel_set_destination(txChannel, (uint32_t)&txBuffer, DMA_CHANNEL-notes);
dma_channel_set_trigger_action(txChannel, DMA_TRIGGER_ACTION_BEAT, DMA_BEAT_SIZE_BYTE);
dma_channel_set_burst_length(txChannel, 1);
dma_channel_enable(txChannel);
dma_channel_set_source(rxChannel, (uint32_t)&rxBuffer, DMA_CHANNEL_notes);
dma_channel_set_destination(rxChannel, (uint32_t)&USART->DATA.reg);
dma_channel_set_trigger_action(rxChannel, DMA_TRIGGER_ACTION_BEAT, DMA_BEAT_SIZE_BYTE);
dma_channel_set_burst_length(rxChannel, 1);
dma_channel_enable(rxChannel);
}
UART通信接口的初始化包括配置时钟、复位USART模块、设置工作模式、配置波特率、数据格式、中断和DMA等。以下是一个完整的初始化函数示例:
// UART初始化函数
void UART_Init(SercomUsart *USART, uint32_t baudRate, uint8_t dataBits, uint8_t stopBits, uint8_t parity, DmaChannel *txChannel, DmaChannel *rxChannel, uint32_t gclk) {
// 配置时钟
gclk_configure(GCLK_CLKCTRL_ID_SERCOM0, gclk, GCLK_GEN_1, GCLK_CLKCTRL_CLKEN);
// 复位USART模块
usart_reset(USART);
// 设置工作模式为UART
usart_set_mode(USART, USART_MODE_USART_RX_TX);
// 配置波特率
UART_SetBaudRate(USART, baudRate, gclk);
// 配置数据格式
UART_SetDataFormat(USART, dataBits, stopBits, parity);
// 配置中断
UART_SetInterrupt(USART, USART_INTENSET_RXC | USART_INTENSET_TXC);
// 配置DMA传输
UART_SetDMA(USART, txChannel, rxChannel);
// 启用USART模块
usart_enable(USART);
}
在初始化完成后,可以使用UART接口进行数据的发送和接收。以下是一些常见的发送和接收函数示例:
发送数据的函数通过将数据写入USART的数据寄存器来实现。如果启用了DMA传输,可以通过DMA通道进行发送。
// UART发送数据
void UART_SendData(SercomUsart *USART, uint8_t *data, uint32_t length) {
for (uint32_t i = 0; i < length; i++) {
// 等待USART准备好发送数据
while (!usart_is_tx_ready(USART)) {
// 处理其他任务或等待
}
// 发送数据
usart_write(USART, data[i]);
}
}
// UART通过DMA发送数据
void UART_SendDataDMA(SercomUsart *USART, uint8_t *data, uint32_t length, DmaChannel *txChannel) {
// 配置DMA传输
dma_channel_set_source(txChannel, (uint32_t)data, DMA_CHANNEL_notes);
dma_channel_set_destination(txChannel, (uint32_t)&USART->DATA.reg);
dma_channel_set_transfer_length(txChannel, length);
dma_channel_set_callback(txChannel, DMA_CALLBACK_TRANSFER_DONE, UART_TransferDoneCallback);
// 开始DMA传输
dma_channel_start_transfer(txChannel);
}
// DMA传输完成回调函数
void UART_TransferDoneCallback(DmaChannel *txChannel) {
// 处理传输完成后的任务
// 例如:关闭DMA通道,发送完成标志等
dma_channel_disable(txChannel);
}
接收数据的函数通过从USART的数据寄存器读取数据来实现。如果启用了DMA传输,可以通过DMA通道进行接收。
// UART接收数据
uint32_t UART_ReceiveData(SercomUsart *USART, uint8_t *data, uint32_t length) {
uint32_t receivedLength = 0;
for (uint32_t i = 0; i < length; i++) {
// 等待USART准备好接收数据
while (!usart_is_rx_ready(USART)) {
// 处理其他任务或等待
}
// 接收数据
data[i] = usart_read(USART);
receivedLength++;
}
return receivedLength;
}
// UART通过DMA接收数据
void UART_ReceiveDataDMA(SercomUsart *USART, uint8_t *data, uint32_t length, DmaChannel *rxChannel) {
// 配置DMA传输
dma_channel_set_source(rxChannel, (uint32_t)&USART->DATA.reg);
dma_channel_set_destination(rxChannel, (uint32_t)data, DMA_CHANNEL_notes);
dma_channel_set_transfer_length(rxChannel, length);
dma_channel_set_callback(rxChannel, DMA_CALLBACK_TRANSFER_DONE, UART_ReceiveDoneCallback);
// 开始DMA传输
dma_channel_start_transfer(rxChannel);
}
// DMA接收完成回调函数
void UART_ReceiveDoneCallback(DmaChannel *rxChannel) {
// 处理接收完成后的任务
// 例如:关闭DMA通道,接收完成标志等
dma_channel_disable(rxChannel);
}
中断处理是UART通信接口中非常重要的部分,可以提高数据传输的实时性和可靠性。以下是一个中断处理函数的示例:
// UART中断处理函数
void UART_Handler(SercomUsart *USART) {
// 检查接收中断
if (usart_is_rx_interrupt_enabled(USART) && usard_is_rx_ready(USART)) {
uint8_t receivedData = usart_read(USART);
// 处理接收到的数据
// 例如:存储到缓冲区,触发事件等
process_received_data(receivedData);
}
// 检查发送中断
if (usart_is_tx_interrupt_enabled(USART) && usart_is_tx_ready(USART)) {
// 获取要发送的数据
uint8_t dataToSend = get_data_to_send();
// 发送数据
usart_write(USART, dataToSend);
// 检查发送缓冲区是否为空
if (is_tx_buffer_empty()) {
// 禁用发送中断
usart_disable_tx_interrupt(USART);
}
}
}
// 处理接收到的数据
void process_received_data(uint8_t data) {
// 例如:将数据存储到缓冲区
rxBuffer[rxBufferIndex] = data;
rxBufferIndex++;
// 检查缓冲区是否已满
if (rxBufferIndex >= RX_BUFFER_SIZE) {
// 处理缓冲区满的情况
rxBufferIndex = 0;
}
}
// 获取要发送的数据
uint8_t get_data_to_send() {
// 例如:从发送缓冲区获取数据
uint8_t data = txBuffer[txBufferIndex];
txBufferIndex++;
// 检查缓冲区是否已空
if (txBufferIndex >= TX_BUFFER_SIZE) {
txBufferIndex = 0;
}
return data;
}
// 检查发送缓冲区是否为空
bool is_tx_buffer_empty() {
return (txBufferIndex == 0);
}
在某些应用场景中,可能需要根据通信伙伴的波特率进行自适应。通过读取和解析接收到的数据,可以动态调整波特率。
// UART波特率自适应
void UART_AdaptiveBaudRate(SercomUsart *USART, uint32_t *baudRate, uint32_t gclk) {
// 读取接收到的数据
uint8_t receivedData = usart_read(USART);
// 解析数据,获取新的波特率
uint32_t newBaudRate = parse_baud_rate(receivedData);
// 设置新的波特率
UART_SetBaudRate(USART, newBaudRate, gclk);
// 更新波特率变量
*baudRate = newBaudRate;
}
// 解析波特率
uint32_t parse_baud_rate(uint8_t data) {
// 例如:解析数据中的波特率信息
// 这里假设数据的前4位表示波特率
uint32_t baudRate = (data >> 4) & 0x0F;
// 根据解析结果返回对应的波特率
switch (baudRate) {
case 0x01:
return 9600;
case 0x02:
return 115200;
default:
return 9600; // 默认波特率
}
}
流控制用于在通信过程中控制数据的传输速率,防止接收方数据缓冲区溢出。SAM L系列单片机的USART模块支持硬件流控制。
// 启用硬件流控制
void UART_EnableHardwareFlowControl(SercomUsart *USART) {
// 配置流控制引脚
pinmux_pin_set_config(USART->RXPO, PINMUX_FUNC_A);
pinmux_pin_set_config(USART->TXPO, PINMUX_FUNC_A);
// 启用硬件流控制
USART->CTRLA.bit.DORD = 1; // 使能数据有序传输
USART->CTRLA.bit.SAMPR = 1; // 使能采样调整
USART->CTRLB.bit.RXEN = 1; // 使能接收
USART->CTRLB.bit.TXEN = 1; // 使能发送
USART->CTRLB.bit.RXINV = 1; // 使能接收引脚反相
USART->CTRLB.bit.TXINV = 1; // 使能发送引脚反相
USART->CTRLB.bit.RXDIS = 1; // 使能接收禁用
USART->CTRLB.bit.TXDIS = 1; // 使能发送禁用
USART->CTRLB.bit.PMODE = 1; // 使能奇偶校验
USART->CTRLB.bit.PAR = USART_CTRLB_PAR_EVEN; // 选择偶校验
USART->CTRLB.bit.CTSEN = 1; // 使能CTS流控制
USART->CTRLB.bit.RTSEN = 1; // 使能RTS流控制
}
// 禁用硬件流控制
void UART_DisableHardwareFlowControl(SercomUsart *USART) {
// 禁用流控制引脚
pinmux_pin_set_config(USART->RXPO, PINMUX_FUNC_NONE);
pinmux_pin_set_config(USART->TXPO, PINMUX_FUNC_NONE);
// 禁用硬件流控制
USART->CTRLB.bit.CTSEN = 0; // 禁用CTS流控制
USART->CTRLB.bit.RTSEN = 0; // 禁用RTS流控制
}
在某些复杂的应用中,可能需要同时使用多个USART模块。以下是一个示例,展示了如何同时配置和使用两个USART模块。
// 多USART模块初始化
void UART_InitMultiple(SercomUsart *USART1, SercomUsart *USART2, uint32_t baudRate, uint8_t dataBits, uint8_t stopBits, uint8_t parity, DmaChannel *txChannel1, DmaChannel *rxChannel1, DmaChannel *txChannel2, DmaChannel *rxChannel2, uint32_t gclk) {
// 初始化USART1
UART_Init(USART1, baudRate, dataBits, stopBits, parity, txChannel1, rxChannel1, gclk);
// 初始化USART2
UART_Init(USART2, baudRate, dataBits, stopBits, parity, txChannel2, rxChannel2, gclk);
}
// 多USART模块发送数据
void UART_SendDataMultiple(SercomUsart *USART1, SercomUsart *USART2, uint8_t *data, uint32_t length) {
// 发送数据到USART1
UART_SendData(USART1, data, length);
// 发送数据到USART2
UART_SendData(USART2, data, length);
}
// 多USART模块接收数据
uint32_t UART_ReceiveDataMultiple(SercomUsart *USART1, SercomUsart *USART2, uint8_t *data, uint32_t length) {
uint32_t receivedLength1 = UART_ReceiveData(USART1, data, length);
uint32_t receivedLength2 = UART_ReceiveData(USART2, data + receivedLength1, length - receivedLength1);
return receivedLength1 + receivedLength2;
}
在开发过程中,调试和测试UART通信接口是非常重要的。可以通过串行终端工具(如PuTTY、TeraTerm等)来测试UART通信的正确性。以下是一些调试和测试的步骤:
配置串行终端工具:设置正确的波特率、数据位、停止位和奇偶校验位,与单片机的配置保持一致。
发送数据:在串行终端工具中输入数据,观察单片机的接收情况。
接收数据:在单片机中发送数据,观察串行终端工具的接收情况。
以下是一个简单的测试代码示例,展示了如何在单片机中发送和接收数据,并通过LED灯显示通信状态。
#include "sam.h"
#include "delay.h"
#include "gpio.h"
#include "usart.h"
#include "dma.h"
// 定义UART和DMA通道
SercomUsart *USART = (SercomUsart *)SERCOM0;
DmaChannel *txChannel = (DmaChannel *)DMA_CHANNEL_0;
DmaChannel *rxChannel = (DmaChannel *)DMA_CHANNEL_1;
// 定义发送和接收缓冲区
uint8_t txBuffer[64];
uint8_t rxBuffer[64];
uint32_t gclk = 48000000;
// 定义LED引脚
#define LED_PIN 27
// 初始化LED
void LED_Init() {
gpio_set_pin_direction(LED_PIN, GPIO_DIRECTION_OUT);
gpio_set_pin_level(LED_PIN, 0); // 关闭LED
}
// LED闪烁函数
void LED_Blink(uint32_t times) {
for (uint32_t i = 0; i < times; i++) {
gpio_set_pin_level(LED_PIN, 1); // 点亮LED
delay_ms(1000); // 延时1秒
gpio_set_pin_level(LED_PIN, 0); // 关闭LED
delay_ms(1000); // 延时1秒
}
}
// UART接收完成回调函数
void UART_ReceiveDoneCallback(DmaChannel *rxChannel) {
// 处理接收完成后的任务
// 例如:关闭DMA通道,接收完成标志等
dma_channel_disable(rxChannel);
LED_Blink(1); // 通过LED闪烁表示接收完成
}
// UART发送完成回调函数
void UART_TransferDoneCallback(DmaChannel *txChannel) {
// 处理传输完成后的任务
// 例如:关闭DMA通道,发送完成标志等
dma_channel_disable(txChannel);
LED_Blink(2); // 通过LED闪烁表示发送完成
}
// 主函数
int main(void) {
// 初始化系统时钟
system_clock_config();
// 初始化LED
LED_Init();
// 初始化UART
UART_Init(USART, 115200, 8, 1, UART_PARITY_NONE, txChannel, rxChannel, gclk);
// 初始化测试数据
const char *testData = "Hello, UART!";
uint32_t testDataLength = strlen(testData);
// 通过UART发送测试数据
UART_SendDataDMA(USART, (uint8_t *)testData, testDataLength, txChannel);
// 通过UART接收数据
UART_ReceiveDataDMA(USART, rxBuffer, 64, rxChannel);
while (1) {
// 主循环中可以处理其他任务
}
}
硬件准备:
确保单片机与串行终端工具(如PC上的USB转串口适配器)正确连接。
确保LED引脚连接到适当的GPIO引脚。
软件配置:
设置串行终端工具的波特率为115200,数据位为8,停止位为1,无奇偶校验。
编译并下载上述代码到单片机。
运行测试:
打开串行终端工具,向单片机发送数据。
观察串行终端工具是否接收到单片机发送的“Hello, UART!”。
观察LED是否按预期闪烁,表示发送和接收完成。
在使用UART通信接口时,可能会遇到一些常见问题。以下是一些问题及其解决方法:
问题:接收的数据与发送的数据不一致。
解决方法:
检查波特率配置:确保发送方和接收方的波特率设置一致。
检查数据格式配置:确保数据位、停止位和奇偶校验位设置一致。
检查引脚配置:确保UART引脚正确配置,没有被其他功能占用。
问题:部分数据在传输过程中丢失。
解决方法:
增加缓冲区大小:确保接收缓冲区足够大,以避免数据溢出。
使用中断处理:通过中断处理及时处理接收到的数据,避免CPU负担过重。
启用DMA传输:使用DMA传输可以减轻CPU负担,提高数据传输的可靠性。
问题:通信过程中出现间歇性故障。
解决方法:
检查电源供应:确保单片机和通信设备的电源供应稳定。
检查信号线:确保信号线没有受到干扰,连接良好。
启用硬件流控制:如果通信设备支持,启用硬件流控制可以提高通信的稳定性。
UART通信接口是嵌入式系统中常用的串行通信方式,通过配置波特率、数据格式、中断和DMA等,可以实现高效、可靠的数据传输。在开发过程中,通过串行终端工具和LED等手段进行调试和测试,可以确保UART通信的正确性。希望本文档对您在SAM L系列单片机上开发UART通信接口有所帮助。