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

posted @ 2021-01-17 15:54  不要让自己太懒  阅读(2995)  评论(0编辑  收藏  举报