AT32 DMA实现LIN协议从机读写

10 篇文章 3 订阅

介绍

本地互连网络(LIN)是一种低成本的单线通信协议,广泛用于汽车应用中连接传感器和执行器。本文将探讨如何使用STM32微控制器实现LIN从设备,重点介绍如何通过DMA(直接内存访问)高效地传输和接收LIN帧。

工程概述

LIN从设备的实现涉及配置GPIO引脚、USART和DMA通道,以便于通信。该从设备根据特定的标识符(PID)进行响应,并进行校验和验证以确保数据完整性。我们将涵盖代码的关键组件,并解释它们的功能和配置。

初始化

lin_init()函数设置USART以进行LIN通信,配置TX和RX引脚,并初始化DMA通道以传输和接收数据。

void lin_init(void) {
    gpio_init_type gpio_init_struct; // 初始化引脚配置结构体

    // 启用时钟
    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; // TX引脚
    gpio_init_struct.gpio_pull = GPIO_PULL_NONE; // 无上下拉
    gpio_init(GPIOA, &gpio_init_struct); // 初始化TX引脚
    
    // RX引脚配置
    gpio_init_struct.gpio_mode = GPIO_MODE_INPUT; // 输入模式
    gpio_init_struct.gpio_pins = GPIO_PINS_10; // RX引脚
    gpio_init(GPIOA, &gpio_init_struct); // 初始化RX引脚
    
    // LIN芯片片选引脚配置
    gpio_bits_set(GPIOA, GPIO_PINS_8); // 拉高LIN芯片片选
    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_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); // 启用LIN模式
    
    // 中断配置
    nvic_irq_enable(USART1_IRQn, 0, 0); // 配置串口优先级
    usart_enable(USART1, TRUE); // 使能USART1
    usart_interrupt_enable(USART1, USART_BF_INT, TRUE); // 使能帧中断
    
    // DMA配置
    usart_dma_transmitter_enable(USART1, TRUE);
    usart_dma_receiver_enable(USART1, TRUE);
    wk_dma1_channel1_init(); // 初始化接收DMA通道
    wk_dma1_channel2_init(); // 初始化发送DMA通道
}

DMA配置

DMA通道被配置为外设到内存和内存到外设的数据传输。这使得微控制器能够高效地处理数据,而无需CPU干预,这在实时应用中至关重要。

void wk_dma_channel_config(dma_channel_type* dmax_channely, uint32_t peripheral_base_addr, uint32_t memory_base_addr, uint16_t buffer_size) {
    dmax_channely->dtcnt = buffer_size; // 数据传输计数
    dmax_channely->paddr = peripheral_base_addr; // 外设地址
    dmax_channely->maddr = memory_base_addr; // 内存地址
}

void wk_dma1_channel1_init(void) {
    crm_periph_clock_enable(CRM_DMA1_PERIPH_CLOCK, TRUE); // 启用DMA1时钟
    dma_init_type dma_init_struct;

    dma_reset(DMA1_CHANNEL1); // 重置DMA通道
    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_struct.loop_mode_enable = FALSE; // 禁用循环模式
    dma_init(DMA1_CHANNEL1, &dma_init_struct); // 初始化DMA通道

    // 灵活功能使能
    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); // 启用DMA1时钟
    dma_init_type dma_init_struct;

    dma_reset(DMA1_CHANNEL2); // 重置DMA通道
    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_struct.priority = DMA_PRIORITY_LOW; // 优先级设置
    dma_init_struct.loop_mode_enable = FALSE; // 禁用循环模式
    dma_init(DMA1_CHANNEL2, &dma_init_struct); // 初始化DMA通道

    // 灵活功能使能
    dma_flexible_config(DMA1, FLEX_CHANNEL2, DMA_FLEXIBLE_UART1_TX); // 配置灵活功能
}

接收数据:

LIN_DMA_ReceiveFrame()函数处理LIN帧的接收。它从RX缓冲区读取数据,并验证校验和以确保数据完整性。接收到的PID决定操作模式(写或读)。

void LIN_DMA_ReceiveFrame(uint8_t id, uint8_t *data, uint8_t dataLength) {
    memset(&rxBuffer, 0, sizeof(rxBuffer)); // 清空接收缓冲区
    while (dma_data_number_get(DMA1_CHANNEL1)); // 等待DMA传输完成
    dma_channel_enable(DMA1_CHANNEL1, FALSE); // 禁用DMA通道
    dma_data_number_set(DMA1_CHANNEL1, 3); // 设置传输数据长度
    wk_dma_channel_config(DMA1_CHANNEL1, (uint32_t)&USART1->dt, (uint32_t)rxBuffer, 3); // 配置DMA通道
    dma_channel_enable(DMA1_CHANNEL1, TRUE); // 使能DMA通道

    while(dma_flag_get(DMA1_FDT1_FLAG) == RESET); // 等待传输完成
    if(rxBuffer[2] == LIN_Slave_Write_pid) {
        Lin_Mode = LIN_Mode_SlaveWrite; // 设置为写模式
    }
    if(rxBuffer[2] == LIN_Slave_Read_pid) {
        dma_channel_enable(DMA1_CHANNEL1, FALSE); // 禁用DMA通道
        dma_data_number_set(DMA1_CHANNEL1, (dataLength + 1)); // 设置数据长度
        wk_dma_channel_config(DMA1_CHANNEL1, (uint32_t)&USART1->dt, (uint32_t)rxBuffer, dataLength + 1); // 配置DMA通道
        dma_channel_enable(DMA1_CHANNEL1, TRUE); // 使能DMA通道

        while(dma_flag_get(DMA1_FDT1_FLAG) == RESET); // 等待传输完成
        while (dma_data_number_get(DMA1_CHANNEL1)); // 等待DMA传输完成
        dma_channel_enable(DMA1_CHANNEL1, FALSE); // 禁用DMA通道
        for (uint8_t i = 0; i < dataLength; i++) {
            data[i] = rxBuffer[i]; // 将接收到的数据拷贝到用户数据缓冲区
        }
        Lin_Mode = LIN_Mode_Slave_Idle; // 设置为空闲模式
    }
}

接收数据:

LIN_DMA_SendFrame()函数准备并发送LIN数据帧。在发送之前,它根据数据和保护ID计算校验和。

void LIN_DMA_SendFrame(uint8_t id, uint8_t *data, uint8_t dataLength) {
    uint8_t PID = lin_Check_ProtectedID(id, dataLength); // 计算PID
    uint16_t checksum = 0;

    txBuffer[0] = PID; // 设置PID
    for (uint8_t i = 0; i < dataLength; i++) {
        txBuffer[i + 1] = data[i]; // 拷贝数据
        checksum += data[i]; // 计算校验和
    }
    checksum = (uint8_t)~checksum; // 计算校验和
    txBuffer[dataLength + 1] = checksum; // 设置校验和

    // 启动DMA传输
    dma_channel_enable(DMA1_CHANNEL2, FALSE); // 禁用DMA通道
    dma_data_number_set(DMA1_CHANNEL2, dataLength + 2); // 设置数据长度
    wk_dma_channel_config(DMA1_CHANNEL2, (uint32_t)&USART1->dt, (uint32_t)txBuffer, dataLength + 2); // 配置DMA通道
    dma_channel_enable(DMA1_CHANNEL2, TRUE); // 使能DMA通道
}

触发中断

USART中断处理程序(USART1_IRQHandler())检测同步间隔,并将LIN模式设置为接收数据。

void USART1_IRQHandler() {
    if(usart_flag_get(USART1, USART_BFF_FLAG) != RESET) {
        Lin_Mode = LIN_Mode_Slave_Receive; // 设置为接收模式
        usart_flag_clear(USART1, USART_BFF_FLAG); // 清除中断标志
    }
}

小结

本实现展示了如何在STM32微控制器上有效利用LIN协议,通过DMA高效处理数据。通过利用DMA,我们可以最小化CPU负担,同时确保快速和可靠的通信。这在汽车应用中尤其重要,因为时序和数据完整性至关重要。
工程链接:https://download.csdn.net/download/TLY983074808/89633351

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值