• STM32-基于UART的CAN通讯(使用的是Modbus通讯协议)


     一、工具

      1、硬件:STM32L053R8单片机(HAL库)

      2、编译环境:Atollic TrueSTUDIO for STM32 9.3.0

      3、辅助工具:STM32CubeMX

    二、电路设计

      将UART信号转换成CAN信号以实现CAN通讯,如下图所示:

     三、软件实现

      1、定时器配置

      定时器7的总线时钟是24MHz,该定时器用于判断串口是否接收完Modbus一帧数据。(配置定时器的时候要遵循Modbus的3.5个字符时间,比如:数据位是8位,起始和结束各1位,如果波特率是38400,则时长为:(3.5*10)/38400 ≈1ms)。

    /*
     * bsp_tim7.c
     *
     *  Created on: Dec 19, 2020
     *      Author: Mr.W
     */
    #include "./tim/bsp_tim7.h"#include "cmsis_os.h"
    
    TIM_HandleTypeDef htim7;
    /* 串口接收到的数据长度 */
    uint16_t data_length = 0;
    extern uint16_t r_count;
    
    static void error_handler(void)
    {
        while(1);
    }
    
    static void bsp_tim7_cfg(void)
    {
        TIM_MasterConfigTypeDef sMasterConfig = {0};
    
        /* Peripheral clock enable */
        __HAL_RCC_TIM7_CLK_ENABLE();
    
        /* 定时器设定的是1ms延时产生一次中断 */
          htim7.Instance = TIM7;
          htim7.Init.Prescaler = 23;
          htim7.Init.CounterMode = TIM_COUNTERMODE_UP;
          htim7.Init.Period = 999;
          htim7.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;
          if (HAL_TIM_Base_Init(&htim7) != HAL_OK)
          {
              error_handler();
          }
          sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;
          sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
          if (HAL_TIMEx_MasterConfigSynchronization(&htim7, &sMasterConfig) != HAL_OK)
          {
              error_handler();
          }
        /* 清除中断 */
        __HAL_TIM_CLEAR_IT(&htim7, TIM_IT_UPDATE);
    }
    
    void bsp_tim7_init(void)
    {
        /* TIM7 interrupt Init */
        HAL_NVIC_SetPriority(TIM7_IRQn, 3, 0);
        HAL_NVIC_EnableIRQ(TIM7_IRQn);
    
        bsp_tim7_cfg();
    }
    
    
    /**
      * @brief This function handles TIM22 global interrupt.
      */
    void TIM7_IRQHandler(void)
    {
        HAL_TIM_IRQHandler(&htim7);
    }
    
    void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
    {
        if(htim->Instance == TIM7)
        {
            /* 关闭定时器 */
            HAL_TIM_Base_Stop_IT(&htim7);
            /* 保存接收到的数据长度 */
            data_length = r_count;
            /* 清空数据接收计数器 */
            r_count = 0;
            xSemaphoreGive(xSemaphore);
        }
    }
    /*
     * bsp_tim7.h
     *
     *  Created on: Dec 19, 2020
     *      Author: Mr.W
     */
    
    #ifndef TIM_BSP_TIM7_H_
    #define TIM_BSP_TIM7_H_
    
    #include "stm32l0xx_hal.h"
    
    void bsp_tim7_init(void);
    
    #endif /* TIM_BSP_TIM7_H_ */

      2、串口配置

    /*
     * bsp_uart1.c
     *
     *  Created on: Dec 19, 2020
     *      Author: Mr.W
     */
    #include "./uart/bsp_uart1.h"
    #include "./tim/bsp_tim7.h"
    
    UART_HandleTypeDef huart1;
    
    uint8_t r_data;
    /* 串口接收到的数据 */
    uint16_t r_count = 0;
    
    uint8_t receive_buffer[256];
    extern TIM_HandleTypeDef htim7;
    
    static void error_handler(void)
    {
        while(1);
    }
    
    static void bsp_uart1_gpio_cfg(void)
    {
        GPIO_InitTypeDef GPIO_InitStruct = {0};
    
        __HAL_RCC_GPIOA_CLK_ENABLE();
    
        /**USART1 GPIO Configuration
        PA9     ------> USART1_TX
        PA10     ------> USART1_RX
        */
        GPIO_InitStruct.Pin = GPIO_PIN_9|GPIO_PIN_10;
        GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
        GPIO_InitStruct.Pull = GPIO_NOPULL;
        GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
        GPIO_InitStruct.Alternate = GPIO_AF4_USART1;
        HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
    }
    
    static void bsp_uart1_cfg(void)
    {
        /* Peripheral clock enable */
        __HAL_RCC_USART1_CLK_ENABLE();
    
        huart1.Instance = USART1;
        huart1.Init.BaudRate = 38400 ;
        huart1.Init.WordLength = UART_WORDLENGTH_8B;
        huart1.Init.StopBits = UART_STOPBITS_1;
        huart1.Init.Parity = UART_PARITY_NONE;
        huart1.Init.Mode = UART_MODE_TX_RX;
        huart1.Init.HwFlowCtl = UART_HWCONTROL_NONE;
        huart1.Init.OverSampling = UART_OVERSAMPLING_16;
        huart1.Init.OneBitSampling = UART_ONE_BIT_SAMPLE_DISABLE;
        huart1.AdvancedInit.AdvFeatureInit = UART_ADVFEATURE_NO_INIT;
        if (HAL_UART_Init(&huart1) != HAL_OK)
        {
            error_handler();
        }
    }
    
    void bsp_uart1_init(void)
    {
        /* USART1 interrupt Init */
        HAL_NVIC_SetPriority(USART1_IRQn, 3, 0);
        HAL_NVIC_EnableIRQ(USART1_IRQn);
    
        bsp_uart1_gpio_cfg();
        bsp_uart1_cfg();
    
        HAL_UART_Receive_IT(&huart1, &r_data, 1);
    }
    
    /**
      * @brief This function handles USART1 global interrupt / USART1 wake-up interrupt through EXTI line 25.
      */
    void USART1_IRQHandler(void)
    {
        HAL_UART_IRQHandler(&huart1);
    }
    
    /**
      * @brief  Tx Transfer completed callback
      * @param  UartHandle: UART handle.
      * @note   This example shows a simple way to report end of IT Tx transfer, and
      *         you can add your own implementation.
      * @retval None
      */
    void HAL_UART_TxCpltCallback(UART_HandleTypeDef *UartHandle)
    {
        /* 串口接收使能 */
        huart1.Instance->CR1 |= (1 << 2);
    }
    
    /**
      * @brief  Rx Transfer completed callback
      * @param  UartHandle: UART handle
      * @note   This example shows a simple way to report end of IT Rx transfer, and
      *         you can add your own implementation.
      * @retval None
      */
    void HAL_UART_RxCpltCallback(UART_HandleTypeDef *UartHandle)
    {
        receive_buffer[r_count++] = r_data;
        /* 定时器计数器清零 */
        __HAL_TIM_SET_COUNTER(&htim7, 0);
        /* 开启定时器 */
        HAL_TIM_Base_Start_IT(&htim7);
        /* 打开接收中断 */
        HAL_UART_Receive_IT(&huart1, &r_data, 1);
    }
    
    /**
      * @brief  UART error callbacks
      * @param  UartHandle: UART handle
      * @note   This example shows a simple way to report transfer error, and you can
      *         add your own implementation.
      * @retval None
      */
    void HAL_UART_ErrorCallback(UART_HandleTypeDef *UartHandle)
    {
    }
    /*
     * bsp_uart1.h
     *
     *  Created on: Dec 19, 2020
     *      Author: Mr.W
     */
    
    #ifndef UART_BSP_UART1_H_
    #define UART_BSP_UART1_H_
    
    #include "stm32l0xx_hal.h"
    
    void bsp_uart1_init(void);
    
    #endif /* UART_BSP_UART1_H_ */

      3、Modbus协议实现

    /*
     * modbus_rtu.c
     *
     *  Created on: Dec 20, 2020
     *      Author: Mr.W
     */
    #include "modbus_rtu.h"
    #include "./tim/bsp_tim7.h"
    #include "./uart/bsp_uart1.h"
    #include "cmsis_os.h"
    
    #define SLAVE_ADDR            1
    
    extern UART_HandleTypeDef huart1;
    extern uint8_t receive_buffer[256];
    uint8_t transfer_buffer[256];
    extern uint16_t data_length;
    
    //////////////////////////////CRC16高位码表///////////////////////////////
    const uint8_t auch_crc_hi[] =
    {
        0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81,
        0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0,
        0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01,
        0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41,
        0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81,
        0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0,
        0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01,
        0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,
        0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81,
        0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0,
        0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01,
        0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
        0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81,
        0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0,
        0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01,
        0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
        0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81,
        0x40
    };
    
    ///////////////////////////////CRC16低位码表//////////////////////////////
    const uint8_t auch_crc_lo[] =
    {
        0x00, 0xC0, 0xC1, 0x01, 0xC3, 0x03, 0x02, 0xC2, 0xC6, 0x06, 0x07, 0xC7, 0x05, 0xC5, 0xC4,
        0x04, 0xCC, 0x0C, 0x0D, 0xCD, 0x0F, 0xCF, 0xCE, 0x0E, 0x0A, 0xCA, 0xCB, 0x0B, 0xC9, 0x09,
        0x08, 0xC8, 0xD8, 0x18, 0x19, 0xD9, 0x1B, 0xDB, 0xDA, 0x1A, 0x1E, 0xDE, 0xDF, 0x1F, 0xDD,
        0x1D, 0x1C, 0xDC, 0x14, 0xD4, 0xD5, 0x15, 0xD7, 0x17, 0x16, 0xD6, 0xD2, 0x12, 0x13, 0xD3,
        0x11, 0xD1, 0xD0, 0x10, 0xF0, 0x30, 0x31, 0xF1, 0x33, 0xF3, 0xF2, 0x32, 0x36, 0xF6, 0xF7,
        0x37, 0xF5, 0x35, 0x34, 0xF4, 0x3C, 0xFC, 0xFD, 0x3D, 0xFF, 0x3F, 0x3E, 0xFE, 0xFA, 0x3A,
        0x3B, 0xFB, 0x39, 0xF9, 0xF8, 0x38, 0x28, 0xE8, 0xE9, 0x29, 0xEB, 0x2B, 0x2A, 0xEA, 0xEE,
        0x2E, 0x2F, 0xEF, 0x2D, 0xED, 0xEC, 0x2C, 0xE4, 0x24, 0x25, 0xE5, 0x27, 0xE7, 0xE6, 0x26,
        0x22, 0xE2, 0xE3, 0x23, 0xE1, 0x21, 0x20, 0xE0, 0xA0, 0x60, 0x61, 0xA1, 0x63, 0xA3, 0xA2,
        0x62, 0x66, 0xA6, 0xA7, 0x67, 0xA5, 0x65, 0x64, 0xA4, 0x6C, 0xAC, 0xAD, 0x6D, 0xAF, 0x6F,
        0x6E, 0xAE, 0xAA, 0x6A, 0x6B, 0xAB, 0x69, 0xA9, 0xA8, 0x68, 0x78, 0xB8, 0xB9, 0x79, 0xBB,
        0x7B, 0x7A, 0xBA, 0xBE, 0x7E, 0x7F, 0xBF, 0x7D, 0xBD, 0xBC, 0x7C, 0xB4, 0x74, 0x75, 0xB5,
        0x77, 0xB7, 0xB6, 0x76, 0x72, 0xB2, 0xB3, 0x73, 0xB1, 0x71, 0x70, 0xB0, 0x50, 0x90, 0x91,
        0x51, 0x93, 0x53, 0x52, 0x92, 0x96, 0x56, 0x57, 0x97, 0x55, 0x95, 0x94, 0x54, 0x9C, 0x5C,
        0x5D, 0x9D, 0x5F, 0x9F, 0x9E, 0x5E, 0x5A, 0x9A, 0x9B, 0x5B, 0x99, 0x59, 0x58, 0x98, 0x88,
        0x48, 0x49, 0x89, 0x4B, 0x8B, 0x8A, 0x4A, 0x4E, 0x8E, 0x8F, 0x4F, 0x8D, 0x4D, 0x4C, 0x8C,
        0x44, 0x84, 0x85, 0x45, 0x87, 0x47, 0x46, 0x86, 0x82, 0x42, 0x43, 0x83, 0x41, 0x81, 0x80,
        0x40
    };
    
    void modbus_rtu_init(void)
    {
        /* 定时器用于获取modbus每一帧数据 */
        bsp_tim7_init();
        /* 串口用于modbus收发数据 */
        bsp_uart1_init();
    }
    
    uint16_t crc16(uint8_t *pdata, uint8_t length)
    {
        uint8_t crc_hi = 0xff;                             // CRC高位寄存器初始化
        uint8_t crc_lo = 0xff;                             // CRC低位寄存器初始化
        uint8_t index;                                     // will index into CRC lookup table
    
        while(length--)                                 // pass through message buffer
        {
            index = crc_hi ^ *pdata++;                     // calculate the CRC
            crc_hi = crc_lo ^ auch_crc_hi[index];
            crc_lo = auch_crc_lo[index];
        }
        return ((crc_hi<<8) | crc_lo);
    }
    
    static uint8_t modbus_rtu_slave_read_data(
            uint8_t slave_addr,        /* 设备地址 */
            uint8_t fun_code,         /* 功能码 */
            uint16_t reg_addr,         /* 寄存器地址 */
            uint16_t *pdata,         /* 数据指针 */
            uint8_t size)            /* 数据大小 */
    {
        uint16_t crc;
        uint8_t i;
    
        transfer_buffer[0] = slave_addr;
        transfer_buffer[1] = fun_code;
        transfer_buffer[2] = (size*2);
        if(pdata != NULL)
        {
            for(i = 0; i < size; i++)
            {
                taskENTER_CRITICAL();
                transfer_buffer[(i << 1) + 3] = ((pdata[reg_addr + i]>>8)&0xFF);
                transfer_buffer[(i << 1) + 4] = (pdata[reg_addr + i]&0xFF);
                taskEXIT_CRITICAL();
            }
        }
        crc = crc16(transfer_buffer, (size*2 + 3));
        transfer_buffer[size*2 + 3] = (crc>>8)&0xFF;
        transfer_buffer[size*2 + 4] = crc&0xFF;
        /* 发送数据 */
        HAL_UART_Transmit_IT(&huart1, transfer_buffer, (size*2 + 5));
        return 0;
    }
    
    
    static uint8_t modbus_rtu_slave_write_data_response(
            uint8_t slave_addr,        /* 设备地址 */
            uint8_t fun_code,         /* 功能码 */
            uint16_t reg_addr,         /* 寄存器地址 */
            uint8_t size)            /* 数据大小 */
    {
        uint16_t crc;
    
        transfer_buffer[0] = slave_addr;
        transfer_buffer[1] = fun_code;
        transfer_buffer[2] = ((reg_addr>>8)&0xFF);
        transfer_buffer[3] = (reg_addr&0xFF);
        transfer_buffer[4] = ((size>>8)&0xFF);
        transfer_buffer[5] = (size&0xFF);
        crc = crc16(transfer_buffer, 6);
        transfer_buffer[6] = ((crc>>8)&0xFF);
        transfer_buffer[7] = (crc&0xFF);
        /* 发送数据 */
        HAL_UART_Transmit_IT(&huart1, transfer_buffer, 8);
        return 0;
    }
    
    uint8_t modbus_rtu_slave_analysis_data(void)
    {
        uint8_t slave_addr;
        uint8_t fun_code;
        uint16_t reg_addr, length;
        uint16_t crc_value, crc_value2;
        uint8_t i;
    
        slave_addr = receive_buffer[0];
        fun_code = receive_buffer[1];
        reg_addr = ((receive_buffer[2] << 8) + receive_buffer[3]);
        length = ((receive_buffer[4] << 8) + receive_buffer[5]);
    
        if(slave_addr == SLAVE_ADDR)
        {
            crc_value = ((receive_buffer[data_length - 2] << 8) + receive_buffer[data_length - 1]);
            crc_value2 = crc16(receive_buffer, data_length - 2);
            if(crc_value != crc_value2)
            {
                return 0;
            }
            switch(fun_code)
            {
                case 0x03:                /* 读多个保持寄存器 */
                    if((reg_addr >= MEASURE_START_ADDR) && ((reg_addr + length) <= (MEASURE_START_ADDR + sizeof(MEASURE_DATA_TYPE)/2)))
                    {
                        /* 关闭串口接收 */
                        huart1.Instance->CR1 &= ~(1 << 2);
                        /* 响应并将数据发送给主机 */
                        modbus_rtu_slave_read_data(slave_addr, fun_code, (reg_addr - MEASURE_START_ADDR), mea_data.data, length);
                    }
                    else
                    {
                        return 0;
                    }
    
                    break;
                case 0x10:                /* 写多个保持寄存器 */
                    if((reg_addr >= MEASURE_START_ADDR) && ((reg_addr + length) <= (MEASURE_START_ADDR + sizeof(MEASURE_DATA_TYPE)/2)))
                    {
                        /* 关闭串口接收 */
                        huart1.Instance->CR1 &= ~(1 << 2);
                        for(i = 0; i < length; i++)
                        {
                            taskENTER_CRITICAL();
                            mea_data.data[reg_addr - MEASURE_START_ADDR + i] = (receive_buffer[i*2 + 7] << 8)|receive_buffer[i*2 + 8];
                            taskEXIT_CRITICAL();
                        }
                        /* 响应主机 */
                        modbus_rtu_slave_write_data_response(slave_addr, fun_code, (reg_addr - MEASURE_START_ADDR), length);
                    }
                    else
                    {
                        return 0;
                    }
                    break;
                default:
                    break;
            }
        }
        else
        {
            return 0;
        }
    
        return 0;
    }
    /*
     * modbus_rtu.h
     *
     *  Created on: Dec 20, 2020
     *      Author: Mr.W
     */
    
    #ifndef MODBUS_RTU_H_
    #define MODBUS_RTU_H_
    
    #include "stm32l0xx_hal.h"
    
    void modbus_rtu_init(void);
    uint8_t modbus_rtu_slave_analysis_data(void);
    
    #endif /* MODBUS_RTU_H_ */

      4、主函数

    /**
      * @brief  The application entry point.
      * @retval int
      */
    int main(void)
    {
        /* MCU Configuration--------------------------------------------------------*/
        /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
        HAL_Init();
        /* Initialize all configured peripherals */
        MX_GPIO_Init();
        /* modbus通讯初始化 */
        modbus_rtu_init();
        /* 创建二值信号量,初始信号资源为0 */
        xSemaphore = xSemaphoreCreateBinary();
        /* Create the thread(s) */
        /* definition and creation of myTask01 */
        osThreadDef(myTask01, StartTask01, osPriorityNormal, 0, 256);
        myTask01Handle = osThreadCreate(osThread(myTask01), NULL);
        /* Start scheduler */
        osKernelStart();
        /* We should never get here as control is now taken by the scheduler */
        /* Infinite loop */
        while (1)
        {
        }
    }

      5、实现数据接收和发送

      数据类型,需要自己补充。

    #pragma pack(1)
    
    typedef union
    {
        uint16_t data[35];
        struct
        {
            
        }param;
    }MEASURE_DATA_TYPE;
    
    #pragma pack()
    MEASURE_DATA_TYPE mea_data;
    /**
      * @brief  Function implementing the myTask01 thread.
      * @param  argument: Not used 
      * @retval None
      */
    void StartTask01(void const * argument)
    {
        /* Infinite loop */
        for(;;)
        {
            /* 等待主机发送一帧数据 */
            if(xSemaphoreTake(xSemaphore, (TickType_t)portMAX_DELAY ) == pdTRUE )
            {
                /* 解析数据 */
                modbus_rtu_slave_analysis_data();
            }
        }
    }

    #end

  • 相关阅读:
    php代码中注释的含义
    MySql-count(*)与count(id)与count(字段)之间的执行结果和性能分析
    mysql通配符进行模糊查询
    我的娃,我的宝贝
    Yii 2.0 query模式语法
    Yii 2.0版本调试输出SQL语句
    mysql高效率随机获取n条数据写法
    mysql语句中判断是否包含某字符串的方法
    应该让老婆多休息
    win10常用快捷键总结
  • 原文地址:https://www.cnblogs.com/wenhao-Web/p/14289196.html
Copyright © 2020-2023  润新知