使用 AT32 DMA实现 LIN 主机读写

10 篇文章 3 订阅

概要

在现代汽车中,LIN(局域内部网络)通信协议广泛应用于车载网络系统。LIN 是一种低成本的串行通信协议,主要用于车载应用中的简单设备通信。本文中,我们将介绍如何使用 AT32 微控制器和 DMA(直接存储器访问)实现 LIN 主机的读写。

LIN 协议概述

LIN 协议的基本框架包括以下几个关键要素:

  • 主机与从机:LIN 网络中有一个主机和多个从机。主机负责控制通信,调度从机的工作。
  • 数据帧:数据通过 LIN 帧进行传输,每个帧包含标识符、数据和校验位
  • 波特率:LIN 通常使用 20 Kbps 的波特率,适合短距离通信。。

代码结构

我们的代码分为几个主要部分:

  • 初始化函数:配置 GPIO 和 USART 以支持 LIN 通信。
  • DMA 配置:设置 DMA 通道以实现高效的数据传输。
  • LIN 帧接收与发送:实现接收和发送 LIN 数据帧的功能。

初始化函数

在 lin_init() 函数中,我们首先启用相关的时钟并配置 GPIO 引脚以支持 UART 通信。我们将 TX 引脚配置为推挽输出模式,RX 引脚配置为输入模式。然后,我们配置 USART,包括波特率和数据格式:

void lin_init(void)
{
    gpio_init_type gpio_init_struct;

    // 启用 USART1 和 GPIOA 的时钟
    crm_periph_clock_enable(CRM_USART1_PERIPH_CLOCK, TRUE);
    crm_periph_clock_enable(CRM_GPIOA_PERIPH_CLOCK, TRUE);

    // 配置 TX 引脚
    gpio_default_para_init(&gpio_init_struct);
    gpio_init_struct.gpio_drive_strength = GPIO_DRIVE_STRENGTH_MODERATE;
    gpio_init_struct.gpio_out_type = GPIO_OUTPUT_PUSH_PULL;
    gpio_init_struct.gpio_mode = GPIO_MODE_MUX;
    gpio_init_struct.gpio_pins = GPIO_PINS_9;
    gpio_init_struct.gpio_pull = GPIO_PULL_NONE;
    gpio_init(GPIOA, &gpio_init_struct);

    // 配置 RX 引脚
    gpio_init_struct.gpio_mode = GPIO_MODE_INPUT;
    gpio_init_struct.gpio_pins = GPIO_PINS_10;
    gpio_init(GPIOA, &gpio_init_struct);

    // 配置 LIN 芯片选择引脚
    gpio_bits_set(GPIOA, GPIO_PINS_8);
    gpio_init_struct.gpio_mode = GPIO_MODE_OUTPUT;
    gpio_init_struct.gpio_pins = GPIO_PINS_8;
    gpio_init(GPIOA, &gpio_init_struct);

    // 配置 USART
    usart_init(USART1, Lin_Speed, USART_DATA_8BITS, USART_STOP_1_BIT);
    usart_transmitter_enable(USART1, TRUE);
    usart_receiver_enable(USART1, TRUE);
    usart_parity_selection_config(USART1, USART_PARITY_NONE);
    usart_break_bit_num_set(USART1, USART_BREAK_11BITS);
    usart_lin_mode_enable(USART1, TRUE);
    usart_enable(USART1, TRUE);
    usart_dma_transmitter_enable(USART1, TRUE);
    usart_dma_receiver_enable(USART1, TRUE);

    wk_dma1_channel1_init();
    wk_dma1_channel2_init();
}

DMA 配置

我们通过 DMA 实现高效的数据接收和发送。wk_dma1_channel1_init() 和 wk_dma1_channel2_init() 函数中,我们分别初始化用于接收和发送的 DMA 通道:

void wk_dma1_channel1_init(void)
{
    crm_periph_clock_enable(CRM_DMA1_PERIPH_CLOCK, TRUE);
    dma_init_type dma_init_struct;

    dma_reset(DMA1_CHANNEL1);
    dma_default_para_init(&dma_init_struct);
    dma_init_struct.direction = DMA_DIR_PERIPHERAL_TO_MEMORY;
    dma_init_struct.memory_data_width = DMA_MEMORY_DATA_WIDTH_BYTE;
    dma_init_struct.memory_inc_enable = TRUE;
    dma_init_struct.peripheral_data_width = DMA_PERIPHERAL_DATA_WIDTH_BYTE;
    dma_init_struct.peripheral_inc_enable = FALSE;
    dma_init_struct.priority = DMA_PRIORITY_LOW;
    dma_init(DMA1_CHANNEL1, &dma_init_struct);
    dma_flexible_config(DMA1, FLEX_CHANNEL1, DMA_FLEXIBLE_UART1_RX);
}

void wk_dma1_channel2_init(void)
{
    crm_periph_clock_enable(CRM_DMA1_PERIPH_CLOCK, TRUE);
    dma_init_type dma_init_struct;

    dma_reset(DMA1_CHANNEL2);
    dma_default_para_init(&dma_init_struct);
    dma_init_struct.direction = DMA_DIR_MEMORY_TO_PERIPHERAL;
    dma_init_struct.memory_data_width = DMA_MEMORY_DATA_WIDTH_BYTE;
    dma_init_struct.memory_inc_enable = TRUE;
    dma_init_struct.peripheral_data_width = DMA_PERIPHERAL_DATA_WIDTH_BYTE;
    dma_init_struct.peripheral_inc_enable = FALSE;
    dma_init(DMA1_CHANNEL2, &dma_init_struct);
    dma_flexible_config(DMA1, FLEX_CHANNEL2, DMA_FLEXIBLE_UART1_TX);
}

接收 LIN 帧

在 LIN_DMA_ReceiveFrame() 函数中,我们使用 DMA 接收 LIN 帧并进行校验。通过计算接收到的数据的校验和,确保数据的完整性和准确性:

void LIN_DMA_ReceiveFrame(uint8_t id, uint8_t *data, uint8_t dataLength) {
    dma_channel_enable(DMA1_CHANNEL1, FALSE); 
    dma_data_number_set(DMA1_CHANNEL1, (dataLength + 4)); 
    wk_dma_channel_config(DMA1_CHANNEL1, (uint32_t)&USART1->dt, (uint32_t)rxBuffer, dataLength + 4);
    dma_channel_enable(DMA1_CHANNEL1, TRUE);

    uint8_t Receive_id = rxBuffer[2] & 0x3f;
    uint16_t receivedChecksum = rxBuffer[dataLength + 3];
    uint16_t checksum = 0;

    // 计算校验和
    if (Lin_CheckSum_Mode == CheckSum_Enhance) {
        checksum += rxBuffer[2];
    }
    for (uint8_t i = 0; i < dataLength; i++) {
        checksum += rxBuffer[i + 3];
        if (checksum >= 256) {
            checksum = (checksum + 1) & 0xFF;
        }
    }
    checksum = (uint8_t)~checksum;

    // 校验结果
    if (receivedChecksum == checksum && Receive_id == id) {
        for (uint8_t i = 0; i < dataLength; i++) {
            data[i] = rxBuffer[i + 3];
        }
    }
}

发送 LIN 数据帧

在 LIN_DMA_SendFrame() 函数中,我们构建要发送的 LIN 数据帧并通过 DMA 发送。该函数计算校验和并将数据打包发送到 LIN 网络:

void LIN_DMA_SendFrame(uint8_t id, uint8_t *data, uint8_t dataLength) {
    uint16_t checksum = 0;
    txBuffer[0] = 0x55; // 帧头
    txBuffer[1] = lin_Check_ProtectedID(id, dataLength); // 计算 PID

    for (uint8_t i = 0; i < dataLength; i++) {
        txBuffer[i + 2] = data[i];
        checksum += data[i];
        if (checksum >= 256) {
            checksum = (checksum + 1) & 0xFF;
        }
    }
    if (Lin_CheckSum_Mode == CheckSum_Enhance) {
        checksum += txBuffer[1];
    }
    checksum = (uint8_t)~checksum; // 反码
    txBuffer[dataLength + 2] = checksum; // 添加校验和

    usart_break_send(USART1);	
    while (dma_data_number_get(DMA1_CHANNEL2)); // 等待 DMA 完成

    // 禁用 DMA 通道并设置数据长度
    dma_channel_enable(DMA1_CHANNEL2, FALSE); 
    dma_data_number_set(DMA1_CHANNEL2, (dataLength + 3)); 

    while (usart_flag_get(USART1, USART_TDC_FLAG) == RESET); // 等待发送完成

    // 配置 DMA 通道
    wk_dma_channel_config(DMA1_CHANNEL2, (uint32_t)&USART1->dt, (uint32_t)txBuffer, dataLength + 3);
    dma_channel_enable(DMA1_CHANNEL2, TRUE);
    while (dma_flag_get(DMA1_FDT2_FLAG) == RESET);
    if(dataLength == 0) {
        Lin_Mode = LIN_Mode_MasterRead; // 更新模式
    }
}

小结

在这篇文章中,我们探讨了如何使用 AT32 实现 LIN 主机的读写功能。通过适当的初始化、DMA 配置、数据帧的接收和发送,我们成功地实现了 LIN 通信。
工程链接:https://download.csdn.net/download/TLY983074808/89633351

  • 9
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值