STM32CubeMX+DMA+Modbus从机

目录

一.开发环境

1.硬件开发环境

2.软件开发环境

二.STM32CubMX相关配置

1、GPIO配置

 2、RCC配置

3、SYS配置

4、USART2配置

​5、NVIC配置

6、生成工程

 三.代码部分

1、创建和编写moudbus.c和moudbus.h文件

1.moudbus.h

2.moudbus.c

2、新增USART.C代码

1、编写main.c和main.h代码

1.main.h新增部分

1.main.c新增部分

 四.下载验证


一.开发环境

文章中实现的功能是单片机作为从机与modbuspoll通讯,并通过modbuspoll写入对应寄存器的值改变RGB灯的颜色和亮灭

1.硬件开发环境

主控芯片STM32F103ZET6

RS485收发芯片MAX3485CSA

硬件电路

2.软件开发环境

STM32Cube+moudbus poll+kail

如果不熟悉moudbus poll可以参考以下文章

Modbus调试软件--ModbusPoll、ModbusSlave使用详解

二.STM32CubMX相关配置

1、GPIO配置

如上图所示MAX3485CSA的控制引脚连接到单片机的PC2引脚RE 接收器输出使能控制。当RE 接低电平时,接收器输出使能,RO 输出有效;当RE 接高电平时,接收器输出禁能,RO 为高阻态;DE 驱动器输出使能控制。DE 接高电平时驱动器输出有效,DE 为低电平时输出为高阻态;RE 接高电平且 DE 接低电平时,器件进入低功耗关断模式

 

 2、RCC配置

3、SYS配置

4、USART2配置

  

 5、NVIC配置

6、生成工程

  

 三.代码部分

1、创建和编写moudbus.c和moudbus.h文件

1.moudbus.h

#ifndef __MODBUS_H
#define __MODBUS_H
 #include "main.h"

#define RS485_DE_RE_GPIO_Port GPIOC
#define RS485_DE_RE_Pin      GPIO_PIN_2

typedef struct                        /* 声明一个变量,用于管理变量 */
{
    uint8_t address;                  /* 从机基地址 */
    uint8_t send[100];                /* 发送缓冲区 */
    uint8_t receive[100];             /* 接收缓冲区 */
} modbus_TypeDef;

extern modbus_TypeDef Modbus;         /* 声明modbus的结构体 */


void Modbus_processing(void);
void Function_03(void);	
void Function_06(void);

void Modbus_Init(void);
uint16_t Modbus_CRC16(uint8_t *buffer, uint16_t buffer_length);	
#endif

2.moudbus.c

#include "modbus.h"
#include "usart.h"
//#include "gpio.h"
modbus_TypeDef Modbus;    //定义结构体

/* 寄存器数据 */
uint16_t Reg[8];


/*初始化*/
void Modbus_Init(void)
{
    Modbus.address = 0x01;          /* 地址位 */
}


void Modbus_processing(void)
{
  uint16_t crc,rccrc;                                                          
	if (xUSART2.receiveNUM < 8) return; 
	
	crc = Modbus_CRC16(&Modbus.receive[0],xUSART2.receiveNUM-2);                            //获取到接收的数据进行计算
    rccrc = (Modbus.receive[xUSART2.receiveNUM-2]<<8) + Modbus.receive[xUSART2.receiveNUM-1];
    if(crc == rccrc)                                                                       //校验码相同才可以进入
    {
        if(Modbus.receive[0] == Modbus.address)                                            //判断基地址
          {
           switch(Modbus.receive[1])                                                       //判断功能码
           {
               case 3:      Function_03();  break;
               case 6:      Function_06();  break;
               case 16:     break;
           }
          }
          else if(Modbus.receive[0] == 0)
          {
          
          }
    }
}
/* 功能码03:主机读取从机 */
void Function_03(void)
{
    uint16_t Regadd, Reglen,crc;
    uint8_t i,j;
    i = 0;
    Regadd = (Modbus.receive[2]<<8)+Modbus.receive[3];
    Reglen = (Modbus.receive[4]<<8)+Modbus.receive[5];
    //开始打包数据包
    Modbus.send[i++] = Modbus.address;               
    Modbus.send[i++] = 0x03;                        
    Modbus.send[i++] = Reglen*2;              
    for(j=0;j<Reglen;j++)
    {
        Modbus.send[i++] = Reg[Regadd+j]>>8;         
        Modbus.send[i++] = Reg[Regadd+j]%256;         
    }
    crc = Modbus_CRC16(Modbus.send,i);                
    Modbus.send[i++] = crc>>8;                       
    Modbus.send[i++] = crc%256;                       
	HAL_GPIO_WritePin(RS485_DE_RE_GPIO_Port, RS485_DE_RE_Pin, GPIO_PIN_SET);//使能485芯片
    HAL_UART_Transmit_DMA (&huart2,(uint8_t*)Modbus.send,i);      
	}
/* 功能码06:主机写入从机 */
void Function_06(void)
{
    uint16_t Regadd,crc,val;                         
    uint16_t i;
    i = 0;
    Regadd = (Modbus.receive[2]<<8)+Modbus.receive[3];      
    val = (Modbus.receive[4]<<8)+Modbus.receive[5];        
    Reg[Regadd] = val;                                
    //开始打包回应包
    Modbus.send[i++] = Modbus.address;              
    Modbus.send[i++] = 0x06;                        
    Modbus.send[i++] = Regadd>>8;                   
    Modbus.send[i++] = Regadd%256;
    Modbus.send[i++] = val>>8;                        
    Modbus.send[i++] = val%256;
    crc = Modbus_CRC16(Modbus.send,i);                
    Modbus.send[i++] = crc>>8;                        
    Modbus.send[i++] = crc%256;                   
	HAL_GPIO_WritePin(RS485_DE_RE_GPIO_Port, RS485_DE_RE_Pin, GPIO_PIN_SET);
    HAL_UART_Transmit_DMA (&huart2,(uint8_t*)Modbus.send,i);     
}


/* CRC校验 参数1:数组指针;参数2;长度 */

uint16_t Modbus_CRC16(uint8_t *buffer, uint16_t buffer_length)
{
    uint16_t crc = 0xFFFF;
    for (uint16_t i = 0; i < buffer_length; i++)
    {
        crc ^= (uint16_t)buffer[i];
        for (uint8_t j = 0; j < 8; j++)
        {
            if (crc & 0x0001)
                crc = (crc >> 1) ^ 0xA001;
            else
                crc = crc >> 1;
        }
    }
    crc = (crc >> 8) | (crc << 8);
    return crc;
}

2、新增USART.C代码

#include "usart.h"

/* USER CODE BEGIN 0 */
#include <stdio.h>   //添加头文件
/* USER CODE END 0 */


/* USER CODE BEGIN 1 */
#pragma import(__use_no_semihosting)            //添加打印重定向函数
struct __FILE
{
    int handle;
};                                                         
 
FILE __stdout;                                             
void _sys_exit(int x)
{
    x = x;                                                
}
 
int fputc(int ch, FILE *f)                                 
{

    HAL_UART_Transmit(&huart2, (uint8_t *)&ch, 1, 0x02);
    return ch;
}

/* USER CODE END 1 */

1、编写main.c和main.h代码

1.main.h新增部分

/* Exported types ------------------------------------------------------------*/
/* USER CODE BEGIN ET */
typedef struct                      /* 声明一个变量,用于管理变量 */
{
    uint16_t receiveNUM;            /* 接收字节数;在中段回调函数里被自动赋值;8>0即为接收到的第一帧 */
    uint8_t  receiveData[512];      /* 接受到的数据 */
    uint8_t  BuffTemp[512];         /* 接收缓存;接收到一帧的时候就会清零并(一帧)复制到ReceivedData[ ] */

} xUSARTx_TypeDef;

/* 声明串口结构体 */
extern xUSARTx_TypeDef xUSART2; /* 声明串口1的结构体 */

/* USER CODE END ET */

/* Exported constants --------------------------------------------------------*/

1.main.c新增部分

#include "main.h"
#include "dma.h"
#include "usart.h"
#include "gpio.h"
/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include "modbus.h"          //新增头文件
#include "stdio.h"
#include "string.h"

/* USER CODE END Includes */
/* USER CODE BEGIN PTD */
extern uint16_t Reg[8];  //声明变量

/* USER CODE END PTD */
/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */
xUSARTx_TypeDef xUSART2; /* 定义串口2的结构体 */
/* USER CODE END 0 */
  /* Initialize all configured peripherals */
  MX_GPIO_Init();
  MX_DMA_Init();
  MX_USART2_UART_Init();
  /* USER CODE BEGIN 2 */
	Modbus_Init();          //添加初始化
	Modbus_processing();
	HAL_UARTEx_ReceiveToIdle_DMA(&huart2, xUSART2.BuffTemp, sizeof(xUSART2.BuffTemp));  // 开启DMA空闲中断    
  /* USER CODE END 2 */
  while (1)
  {
//在while循环里添加测试代码,以控制RGB灯为例,低电平点亮
if (Reg[0] & 0x0001) 	{  HAL_GPIO_WritePin(GPIOB, GPIO_PIN_1, GPIO_PIN_RESET);}
else{ HAL_GPIO_WritePin(GPIOB, GPIO_PIN_1, GPIO_PIN_SET);}
if (Reg[1] & 0x0002) 	{  HAL_GPIO_WritePin(GPIOB, GPIO_PIN_0, GPIO_PIN_RESET);}
else{ HAL_GPIO_WritePin(GPIOB, GPIO_PIN_0, GPIO_PIN_SET);}
if (Reg[2] & 0x0003) 	{  HAL_GPIO_WritePin(GPIOB, GPIO_PIN_5, GPIO_PIN_RESET);}
else{ HAL_GPIO_WritePin(GPIOB, GPIO_PIN_5, GPIO_PIN_SET);}
if (Reg[3] & 0x0004) 	{  HAL_GPIO_WritePin(GPIOB, GPIO_PIN_1 |  GPIO_PIN_0| GPIO_PIN_5, GPIO_PIN_RESET);}
else{ HAL_GPIO_WritePin(GPIOB, GPIO_PIN_1 |  GPIO_PIN_0| GPIO_PIN_5, GPIO_PIN_SET);}
if (Reg[4] & 0x0005) 	{  HAL_GPIO_WritePin(GPIOB, GPIO_PIN_1 |  GPIO_PIN_0, GPIO_PIN_RESET);}
else{ HAL_GPIO_WritePin(GPIOB, GPIO_PIN_1 |  GPIO_PIN_0, GPIO_PIN_SET);}
if (Reg[5] & 0x0006) 	{  HAL_GPIO_WritePin(GPIOB, GPIO_PIN_1 |  GPIO_PIN_5, GPIO_PIN_RESET);}
else{ HAL_GPIO_WritePin(GPIOB, GPIO_PIN_1 |  GPIO_PIN_5, GPIO_PIN_SET);}

    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
  }
/* USER CODE BEGIN 4 */
 void HAL_UARTEx_RxEventCallback(UART_HandleTypeDef *huart, uint16_t Size)
{
    if (huart == &huart2)                                                                    // 判断串口
    {
        __HAL_UNLOCK(huart);                                                                 // 解锁串口状态
 
        xUSART2.receiveNUM  = Size;                                                          // 把接收字节数,存入结构体xUSART1.ReceiveNum
        memset(Modbus.receive, 0, sizeof(Modbus.receive));                         // 清除Modbus.receive前一帧接收到的数据
        memset(xUSART2.receiveData, 0, sizeof(xUSART2.receiveData));                         // 清除xUSART1.ReceiveData前一帧接收到的数据
        memcpy(xUSART2.receiveData, xUSART2.BuffTemp, Size);                                 // 把新数据,从临时缓存中,复制到xUSART1.ReceiveData[]
        memcpy(Modbus.receive, xUSART2.BuffTemp, Size);// 把新数据,从临时缓存中,复制到Modbus.receive
        HAL_UARTEx_ReceiveToIdle_DMA(&huart2, xUSART2.BuffTemp, sizeof(xUSART2.BuffTemp));   // 再次开启DMA空闲中断; 每当接收完指定长度,或者产生空闲中断时,就会来到这个
				Modbus_processing(); // 立即处理接收到的请求
		}
		
}
void HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart)
{
    if (huart->Instance == USART2)
    {
        HAL_GPIO_WritePin(RS485_DE_RE_GPIO_Port, RS485_DE_RE_Pin, GPIO_PIN_RESET);
    }
}
/* USER CODE END 4 */

 四.下载验证

通过USB转485转换器连接到电脑,并打开moudbuspoll软件,选择对应com口并设置对应波特率

1、读取测试

因为代上面代码中uint16_t Reg[8]是空的所以寄存器0-7的值都为0

1、写入测试

写入Reg[0]的值为0x0001

写入Reg[1]的值为0x0002

写入Reg[2]的值为0x0003

RGB灯点亮正常 

查看对应寄存器的值

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值