Microchip 系列:SAM L 系列 (基于 ARM Cortex-M0+)_(9).UART通信接口开发

UART通信接口开发

1. UART通信接口简介

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负担。

2. UART通信接口基本配置

在使用SAM L系列单片机的UART通信接口之前,需要进行一些基本的配置。这些配置包括设置波特率、数据格式、中断和DMA等。以下是一些常见的配置步骤:

2.1 配置波特率

波特率是指每秒钟传输的位数,常见的波特率有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;

}

2.2 配置数据格式

数据格式包括数据位、停止位和奇偶校验位。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;

}

2.3 配置中断

UART通信接口可以通过中断来处理数据的发送和接收,减轻CPU的负担。在SAM L系列单片机中,可以通过配置USART模块的中断寄存器来启用中断。


// UART配置中断

void UART_SetInterrupt(SercomUsart *USART, uint8_t interruptMask) {

    // 配置中断使能寄存器

    USART->INTENSET.reg = interruptMask;

}

2.4 配置DMA传输

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);

}

3. UART通信接口的初始化

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);

}

4. UART数据发送与接收

在初始化完成后,可以使用UART接口进行数据的发送和接收。以下是一些常见的发送和接收函数示例:

4.1 发送数据

发送数据的函数通过将数据写入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);

}

4.2 接收数据

接收数据的函数通过从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);

}

5. UART通信接口的中断处理

中断处理是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);

}

6. UART通信接口的高级应用

6.1 波特率自适应

在某些应用场景中,可能需要根据通信伙伴的波特率进行自适应。通过读取和解析接收到的数据,可以动态调整波特率。


// 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; // 默认波特率

    }

}

6.2 流控制

流控制用于在通信过程中控制数据的传输速率,防止接收方数据缓冲区溢出。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流控制

}

6.3 多USART模块同时使用

在某些复杂的应用中,可能需要同时使用多个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;

}

7. UART通信接口的调试与测试

在开发过程中,调试和测试UART通信接口是非常重要的。可以通过串行终端工具(如PuTTY、TeraTerm等)来测试UART通信的正确性。以下是一些调试和测试的步骤:

7.1 使用串行终端工具进行测试

  1. 配置串行终端工具:设置正确的波特率、数据位、停止位和奇偶校验位,与单片机的配置保持一致。

  2. 发送数据:在串行终端工具中输入数据,观察单片机的接收情况。

  3. 接收数据:在单片机中发送数据,观察串行终端工具的接收情况。

7.2 代码示例

以下是一个简单的测试代码示例,展示了如何在单片机中发送和接收数据,并通过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) {

        // 主循环中可以处理其他任务

    }

}

7.3 测试步骤

  1. 硬件准备

    • 确保单片机与串行终端工具(如PC上的USB转串口适配器)正确连接。

    • 确保LED引脚连接到适当的GPIO引脚。

  2. 软件配置

    • 设置串行终端工具的波特率为115200,数据位为8,停止位为1,无奇偶校验。

    • 编译并下载上述代码到单片机。

  3. 运行测试

    • 打开串行终端工具,向单片机发送数据。

    • 观察串行终端工具是否接收到单片机发送的“Hello, UART!”。

    • 观察LED是否按预期闪烁,表示发送和接收完成。

8. UART通信接口的常见问题与解决方法

在使用UART通信接口时,可能会遇到一些常见问题。以下是一些问题及其解决方法:

8.1 数据传输不正确

问题:接收的数据与发送的数据不一致。

解决方法

  • 检查波特率配置:确保发送方和接收方的波特率设置一致。

  • 检查数据格式配置:确保数据位、停止位和奇偶校验位设置一致。

  • 检查引脚配置:确保UART引脚正确配置,没有被其他功能占用。

8.2 数据丢失

问题:部分数据在传输过程中丢失。

解决方法

  • 增加缓冲区大小:确保接收缓冲区足够大,以避免数据溢出。

  • 使用中断处理:通过中断处理及时处理接收到的数据,避免CPU负担过重。

  • 启用DMA传输:使用DMA传输可以减轻CPU负担,提高数据传输的可靠性。

8.3 通信不稳定

问题:通信过程中出现间歇性故障。

解决方法

  • 检查电源供应:确保单片机和通信设备的电源供应稳定。

  • 检查信号线:确保信号线没有受到干扰,连接良好。

  • 启用硬件流控制:如果通信设备支持,启用硬件流控制可以提高通信的稳定性。

9. 总结

UART通信接口是嵌入式系统中常用的串行通信方式,通过配置波特率、数据格式、中断和DMA等,可以实现高效、可靠的数据传输。在开发过程中,通过串行终端工具和LED等手段进行调试和测试,可以确保UART通信的正确性。希望本文档对您在SAM L系列单片机上开发UART通信接口有所帮助。

你可能感兴趣的:(单片机开发,arm开发,嵌入式硬件,单片机,架构,硬件架构)