STM32HAL库RS485-ModBus协议控制伺服电机

news2024/11/28 18:58:50

STM32HAL库RS485-ModBus协议控制伺服电机

一个月前,接手了一个学长的毕设小车,小车采用rs485通信的modbus协议驱动轮毂电机,与往常我学习的pwm控制电机方法大相径庭,在这里以这篇博客记录下该学习过程。

小车主要架构

image-20230422152803354

电机型号

中菱轮毂电机

轮毂驱动器ZLAC8015D

  • ZLAC8015D 的 RS485 支持 Modbus RTU 协议。
  • 驱动器地址为 0-127 可设,默认为 1;
  • 波特率 9600、19200、38400、57600、115200、128000、256000 等 7 种, 可通过软件设置。

默认 115200; 数据位 8,无奇偶校验,停止位 1

RS485通信

RS485接口组成的半双工网络,一般是两线制,多采用屏蔽双绞线传输,这种接线方式为总线式拓扑结构在同一总线上最多可以挂接32个结点。我们知道,最初数据是模拟信号输出简单过程量,后来仪表接口是RS232接口,这种接口可以实现点对点的通信方式,但这种方式不能实现联网功能,随后出现的RS485解决了这个问题。为此本文通过问答的形式详细介绍RS485接口。

RS485_RE为高电平的时候,DE为高电平有效,允许发送数据
RS485_RE为低电平的时候,RE为低电平有效,允许接收数据

485转换芯片可以把输入的串口信号转化成差分信号,也可以差分信号转化成串口信号

信号线A、B

A>B0
B>A1

STM32实现主从机RS485通信

STM32F103ZET6

RS485接口 A接A B接B

image-20230422180220414

从机:

RS485发送 “Hello World”

char Buff[30];
//*******************
while (1)
  {
    /* USER CODE END WHILE */
      HAL_GPIO_WritePin(GPIOD, GPIO_PIN_7, GPIO_PIN_SET); //拉高发送
      sprintf(Buff,"Hello World");
      HAL_UART_Transmit(&huart2, Buff, sizeof(Buff), 0xffff); 
      HAL_Delay(500);
    /* USER CODE BEGIN 3 */
  }

主机:

RS485接收 “Hello World”

虚拟串口打印

HAL_UART_Receive_IT(&huart2, RxBuff,1);
HAL_GPIO_WritePin(GPIOD,GPIO_PIN_7, GPIO_PIN_RESET);  //拉低接收
//*******************************
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
    uint8_t i;
    if(huart->Instance==USART2)
    {
        HAL_UART_Receive_IT(&huart2,RxBuff,10);
        CDC_Transmit_FS(RxBuff, sizeof(RxBuff));
    }
}

image-20230417170911895

Modbus协议

Modbus就是一种用在工业上的简单协议!

大致分为以下几种:

  • Modbus-RTU
  • Modbus-ASCII
  • Modbus-TCP

以上三种协议,一个设备只会有一种协议,该电机使用的是Modbus-RTU。

Modbus是主从方式通信,也就是说,不能同步进行通信,总线上每次只有一个数据进行传输,即主机发送,从机应答,主机不发送,总线上就没有数据通信。

Modbus Poll主机Modbus Slave从机通信

第三方测试软件:vspd、Modbus Poll、Modbus Slave

虚拟一个串口

image-20230422151542599

image-20230422152049651

修改配置

image-20230422152532666

读取数据

发送

Tx:000024- 01 03 00 00 00 0A C5 CD

010300 0000 0AC5 CD
ID号功能码起始地址数据内容校验码

接收

Rx:000143- 01 03 0A 00 01 00 02 00 01 00 00 00 00 37 26

01030A00 01 00 02 00 01 00 00 00 0037 26
ID号功能码字节长度数据内容校验位

发送单条数据

010600 0400 0388 0A
ID号功能码起始地址数据内容校验码

image-20230422160343498

发送多条数据

主机TX:

ID功能码起始地址寄存器长度数据内容校验码
0116(0X10)00 0000 0510字节XX XX

Tx:001200- 01 10 00 03 00 01 02 00 04 A7 A0

ID功能码起始地址数据长度字节长度数据内容校验码
011000 0300 010200 04A7 A0

功能码:

  • 03->读取数据
  • 06->发送单条数据
  • 10->发送多条数据

SSCOM主机Modbus Slave从机通信

读取数据

image-20230422165736981

发送单条数据

image-20230422165911874

发送多条数据

image-20230422170213418

STM32通过RS485完成Modbus协议通信

移植Modbus协议

mbrtu_master.h

#ifndef MBRTU_MASTER_H_
#define MBRTU_MASTER_H_

#include <stdint.h>
#include <stdlib.h>
#include <stdio.h>
#include "mbrtu_master.h"
#include "usart.h"
#include "tim.h"



/
///
/// MODBUS RTU 主机控制结构
///
///
typedef struct
{
	//
	// 收发数据缓存
	//
	uint8_t ucBuf[128];
	
	//
	// 收发数据状态
	//
	uint16_t usStatus;
	
	//
	// 如果使用了RTOS需要进行互斥,那么需要实现以下两个函数的绑定
	//
	void (*lock)(void);
	void (*unlock)(void);
	
	//
	// 微秒延时函数,用于等待超时
	//
	void (*delayms)(uint32_t nms);
	
	//
	// 定时器启动和停止函数
	//
	void (*timerStop)(void);
	void (*timerStart)(void);
	
	//
	// 发送数据函数,可以是串口、TCP等
	//
	uint32_t (*sendData)(const void* buf, uint32_t len);

	//
	// 以下四个回调函数分别是:读线圈、读离散量输入、读保持寄存器、读输入寄存器
	//
	void (*readCoilsCallback)(uint16_t usStartAddr, uint16_t usNum, const uint8_t* pucBitsOfCoilsState, uint16_t usLen);
	void (*readDiscreteInputsCallback)(uint16_t usStartAddr, uint16_t usNum, const uint8_t* pucBitsOfDiscreteInputsState, uint16_t usLen);
	void (*readHoldingRegistersCallback)(uint16_t usStartAddr, uint16_t usNum, const uint16_t* pusHoldingRegistersVal, uint16_t usLen);
	void (*readInputRegistersCallback)(uint16_t usStartAddr, uint16_t usNum, const uint16_t* pusInputRegistersVal, uint16_t usLen);

}MBRTUMaterTypeDef;

/
static void timerStop(void);
static void timerStart(void);
static void delayms(uint32_t nms);
static uint32_t sendData(const void *buf, uint32_t len);
static void readCoilsCallback(uint16_t usStartAddr, uint16_t usNum, const uint8_t *pucBitsOfCoilsState, uint16_t usLen);
static void readDiscreteInputsCallback(uint16_t usStartAddr, uint16_t usNum, const uint8_t *pucBitsOfDiscreteInputsState, uint16_t usLen);
static void readHoldingRegistersCallback(uint16_t usStartAddr, uint16_t usNum, const uint16_t *pusHoldingRegistersVal, uint16_t usLen);
static void readInputRegistersCallback(uint16_t usStartAddr, uint16_t usNum, const uint16_t *pusInputRegistersVal, uint16_t usLen);

/
///
/// MODBUS RTU 主机 API
///
///
int MBRTUMasterReadCoils(MBRTUMaterTypeDef* psModbus, uint8_t ucSlaveAddress, uint16_t usAddress, uint16_t usNum, uint16_t usTimeout);
int MBRTUMasterReadDiscreteInputs(MBRTUMaterTypeDef* psModbus, uint8_t ucSlaveAddress, uint16_t usAddress, uint16_t usNum, uint16_t usTimeout);
int MBRTUMasterReadHoldingRegisters(MBRTUMaterTypeDef* psModbus, uint8_t ucSlaveAddress, uint16_t usAddress, uint16_t usNum, uint16_t usTimeout);
int MBRTUMasterReadInputRegisters(MBRTUMaterTypeDef* psModbus, uint8_t ucSlaveAddress, uint16_t usAddress, uint16_t usNum, uint16_t usTimeout);
int MBRTUMasterWriteSingleCoil(MBRTUMaterTypeDef* psModbus, uint8_t ucSlaveAddress, uint16_t usAddress, uint8_t ucState, uint16_t usTimeout);
int MBRTUMasterWriteSingleRegister(MBRTUMaterTypeDef* psModbus, uint8_t ucSlaveAddress, uint16_t usAddress, uint16_t usRegVal, uint16_t usTimeout);
int MBRTUMasterWriteMultipleCoils(MBRTUMaterTypeDef* psModbus, uint8_t ucSlaveAddress, uint16_t usAddress, uint16_t usNum, const uint8_t* pucStateBitsBuf, uint16_t usTimeout);
int MBRTUMasterWriteMultipleRegisters(MBRTUMaterTypeDef* psModbus, uint8_t ucSlaveAddress, uint16_t usAddress, uint16_t usNum, const uint16_t* pusRegVal, uint16_t usTimeout);

/
///
/// MODBUS RTU 主机接收数据回调函数和超时回调函数
/// 
/// MBRTUMasterRecvByteISRCallback:放置于串口接收中断中
/// MBRTUMasterTimerISRCallback:放置于定时器超时中断中
///
void MBRTUMasterRecvByteISRCallback(MBRTUMaterTypeDef* psModbus, uint8_t ucByte);
void MBRTUMasterTimerISRCallback(MBRTUMaterTypeDef* psModbus);

#endif /* MBRTU_MASTER_H_ */

mbrtu_master.c

/*
 * mbrtu_master.c
 *
 *  Created on: 2022年4月29日
 *      Author: hello
 */

#include "mbrtu_master.h"

static uint16_t usMBCRC16(uint8_t *pucFrame, uint16_t usLen)
{
    static const uint8_t aucCRCHi[] =
        {
            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};

    static const uint8_t aucCRCLo[] =
        {
            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};

    uint8_t ucCRCHi = 0xFF;
    uint8_t ucCRCLo = 0xFF;
    int iIndex;

    while (usLen--)
    {
        iIndex = ucCRCLo ^ *(pucFrame++);
        ucCRCLo = (uint8_t)(ucCRCHi ^ aucCRCHi[iIndex]);
        ucCRCHi = aucCRCLo[iIndex];
    }
    return (uint16_t)(ucCRCHi << 8 | ucCRCLo);
}

//读取数据
static uint32_t MBRTUMasterRead(MBRTUMaterTypeDef *pMaster, uint8_t ucSlaveAddr, uint8_t ucCmd, uint16_t usStartAddr, uint16_t usNum)
{
    uint16_t crc;

    pMaster->ucBuf[0] = ucSlaveAddr;
    pMaster->ucBuf[1] = ucCmd;
    pMaster->ucBuf[2] = ((usStartAddr & 0XFF00) >> 8);
    pMaster->ucBuf[3] = (usStartAddr & 0XFF);
    pMaster->ucBuf[4] = ((usNum & 0XFF00) >> 8);
    pMaster->ucBuf[5] = (usNum & 0XFF);

    crc = usMBCRC16((uint8_t *)pMaster->ucBuf, 6);
    pMaster->ucBuf[6] = (uint8_t)(crc & 0xFF);
    pMaster->ucBuf[7] = (uint8_t)(crc >> 8);

    return pMaster->sendData(pMaster->ucBuf, 8);
}

/**
 * 主机读取线圈状态
 * @param  ucSlaveAddress 从机地址
 * @param  usAddress      要读取的线圈起始地址
 * @param  usCmd          0x01
 * @param  usNum          要读取的线圈数量
 * @param  usTimeout      超时时间,单位毫秒
 * @return                0:成功  <0:执行失败
 */
int MBRTUMasterReadCoils(MBRTUMaterTypeDef *psModbus, uint8_t ucSlaveAddress, uint16_t usAddress, uint16_t usNum, uint16_t usTimeout)
{
    int ret = -1;
    int delay;

    if (psModbus->lock != NULL)
    {
        psModbus->lock();
    }

    psModbus->usStatus = 0;

    MBRTUMasterRead(psModbus, ucSlaveAddress, 0X01, usAddress, usNum);

    while (usTimeout != 0)
    {
        if (psModbus->usStatus & 0X8000)
        {
            if (psModbus->ucBuf[0] == ucSlaveAddress && psModbus->ucBuf[1] == 0X01)
            {
                psModbus->readCoilsCallback(usAddress, usNum, &psModbus->ucBuf[3], psModbus->ucBuf[2]);
                ret = 0;
            }
            else
            {
                ret = -2;
            }
            psModbus->usStatus = 0;
            break;
        }
        delay = usTimeout > 5 ? 5 : usTimeout;
        usTimeout -= delay;
        psModbus->delayms(delay);
    }

    if (psModbus->unlock != NULL)
    {
        psModbus->unlock();
    }

    return ret;
}

/**
 * 主机读取离散量输入
 * @param  ucSlaveAddress 从机地址
 * @param  usAddress      要读取的离散量起始地址
 * @param  usCmd          0x02
 * @param  usNum          要读取的离散量数量
 * @param  usTimeout      超时时间,单位毫秒
 * @return                0:成功  <0:执行失败
 */
int MBRTUMasterReadDiscreteInputs(MBRTUMaterTypeDef *psModbus, uint8_t ucSlaveAddress, uint16_t usAddress, uint16_t usNum, uint16_t usTimeout)
{
    int ret = -1;
    int delay;

    if (psModbus->lock != NULL)
    {
        psModbus->lock();
    }

    psModbus->usStatus = 0;

    MBRTUMasterRead(psModbus, ucSlaveAddress, 0X02, usAddress, usNum);

    while (usTimeout != 0)
    {
        if (psModbus->usStatus & 0X8000)
        {
            if (psModbus->ucBuf[0] == ucSlaveAddress && psModbus->ucBuf[1] == 0X02)
            {
                psModbus->readDiscreteInputsCallback(usAddress, usNum, &psModbus->ucBuf[3], psModbus->ucBuf[2]);
                ret = 0;
            }
            else
            {
                ret = -2;
            }
            psModbus->usStatus = 0;
            break;
        }
        delay = usTimeout > 5 ? 5 : usTimeout;
        usTimeout -= delay;
        psModbus->delayms(delay);
    }

    if (psModbus->unlock != NULL)
    {
        psModbus->unlock();
    }

    return ret;
}

/**
 * 主机读取保持寄存器!!!!!!!!!!!!
 * @param  ucSlaveAddress 从机地址
 * @param  usAddress      要读取的保持寄存器起始地址
 * @param  usCmd          0x03
 * @param  usNum          要读取的保持寄存器数量
 * @param  usTimeout      超时时间,单位毫秒
 * @return                0:成功  <0:执行失败
 */
int MBRTUMasterReadHoldingRegisters(MBRTUMaterTypeDef *psModbus, uint8_t ucSlaveAddress, uint16_t usAddress, uint16_t usNum, uint16_t usTimeout)
{
    int ret = -1;
    int delay;

    if (psModbus->lock != NULL)
    {
        psModbus->lock();
    }

    psModbus->usStatus = 0;

    MBRTUMasterRead(psModbus, ucSlaveAddress, 0X03, usAddress, usNum);

    while (usTimeout != 0)
    {
        if (psModbus->usStatus & 0X8000)
        {
            if (psModbus->ucBuf[0] == ucSlaveAddress && psModbus->ucBuf[1] == 0X03)
            {
                psModbus->readHoldingRegistersCallback(usAddress, usNum, (const uint16_t *)&psModbus->ucBuf[3], psModbus->ucBuf[2] >> 1);
                ret = 0;
            }
            else
            {
                ret = -2;
            }
            psModbus->usStatus = 0;
            break;
        }
        delay = usTimeout > 5 ? 5 : usTimeout;
        usTimeout -= delay;
        psModbus->delayms(delay);
    }

    if (psModbus->unlock != NULL)
    {
        psModbus->unlock();
    }

    return ret;
}

/**
 * 主机读取输入寄存器
 * @param  ucSlaveAddress 从机地址
 * @param  usAddress      要读取的输入寄存器起始地址
 * @param  usCmd          0x04
 * @param  usNum          要读取的输入寄存器数量
 * @param  usTimeout      超时时间,单位毫秒
 * @return                0:成功  <0:执行失败
 */
int MBRTUMasterReadInputRegisters(MBRTUMaterTypeDef *psModbus, uint8_t ucSlaveAddress, uint16_t usAddress, uint16_t usNum, uint16_t usTimeout)
{
    int ret = -1;
    int delay;

    if (psModbus->lock != NULL)
    {
        psModbus->lock();
    }

    psModbus->usStatus = 0;

    MBRTUMasterRead(psModbus, ucSlaveAddress, 0X04, usAddress, usNum);

    while (usTimeout != 0)
    {
        if (psModbus->usStatus & 0X8000)
        {
            if (psModbus->ucBuf[0] == ucSlaveAddress && psModbus->ucBuf[1] == 0X04)
            {
                psModbus->readInputRegistersCallback(usAddress, usNum, (const uint16_t *)&psModbus->ucBuf[3], psModbus->ucBuf[2] >> 1);
                ret = 0;
            }
            else
            {
                ret = -2;
            }
            psModbus->usStatus = 0;
            break;
        }
        delay = usTimeout > 5 ? 5 : usTimeout;
        usTimeout -= delay;
        psModbus->delayms(delay);
    }

    if (psModbus->unlock != NULL)
    {
        psModbus->unlock();
    }

    return ret;
}

/**
 * 主机写单个线圈
 * @param  ucSlaveAddress 从机地址
 * @param  usAddress      线圈地址
 * @param  usCmd          0x05
 * @param  ucState        要设置的线圈状态,1或者0
 * @param  usTimeout      超时时间,单位毫秒
 * @return                0:成功  <0:执行失败
 */

int MBRTUMasterWriteSingleCoil(MBRTUMaterTypeDef *psModbus, uint8_t ucSlaveAddress, uint16_t usAddress, uint8_t ucState, uint16_t usTimeout)
{
    int ret = -1;
    int delay;
    uint16_t crc;

    if (psModbus->lock != NULL)
    {
        psModbus->lock();
    }

    psModbus->ucBuf[0] = ucSlaveAddress;
    psModbus->ucBuf[1] = 0X05;
    psModbus->ucBuf[2] = usAddress & 0XFF;
    psModbus->ucBuf[3] = usAddress >> 8;
    psModbus->ucBuf[4] = ucState ? 0XFF : 0X00;
    psModbus->ucBuf[5] = 0X00;
    crc = usMBCRC16((uint8_t *)psModbus->ucBuf, 6);
    psModbus->ucBuf[6] = (uint8_t)(crc & 0xFF);
    psModbus->ucBuf[7] = (uint8_t)(crc >> 8);

    psModbus->usStatus = 0;
    psModbus->sendData(psModbus->ucBuf, 8);

    while (usTimeout != 0)
    {
        if (psModbus->usStatus & 0X8000)
        {
            if (psModbus->ucBuf[0] == ucSlaveAddress && psModbus->ucBuf[1] == 0X05)
            {
                ret = 0;
            }
            else
            {
                ret = -2;
            }
            psModbus->usStatus = 0;
            break;
        }
        delay = usTimeout > 5 ? 5 : usTimeout;
        usTimeout -= delay;
        psModbus->delayms(delay);
    }

    if (psModbus->unlock != NULL)
    {
        psModbus->unlock();
    }

    return ret;
}

/**
 * 主机写单个寄存器!!!!!!!!!!!
 * @param  ucSlaveAddress 从机地址
 * @param  usAddress      寄存器地址
 * @param  usCmd          0x06
 * @param  usRegVal       寄存器值
 * @param  usTimeout      超时时间,单位毫秒
 * @return                0:成功  <0:执行失败
 */
//	MBRTUMasterWriteSingleRegister(&MBRTUHandle, 1, RUN_MODE_ADDR, 0x0003, 100);
int MBRTUMasterWriteSingleRegister(MBRTUMaterTypeDef *psModbus, uint8_t ucSlaveAddress, uint16_t usAddress, uint16_t usRegVal, uint16_t usTimeout)
{
    int ret = -1;
    int delay;
    uint16_t crc;

    if (psModbus->lock != NULL)
    {
        psModbus->lock();
    }

    psModbus->ucBuf[0] = ucSlaveAddress;
    psModbus->ucBuf[1] = 0X06;
    psModbus->ucBuf[2] = usAddress & 0XFF;
    psModbus->ucBuf[3] = usAddress >> 8;
    psModbus->ucBuf[4] = usRegVal >> 8;
    psModbus->ucBuf[5] = usRegVal & 0XFF;
    crc = usMBCRC16((uint8_t *)psModbus->ucBuf, 6);
    psModbus->ucBuf[6] = (uint8_t)(crc & 0xFF);
    psModbus->ucBuf[7] = (uint8_t)(crc >> 8);

    psModbus->usStatus = 0;
    psModbus->sendData(psModbus->ucBuf, 8);

    while (usTimeout != 0)
    {
        if (psModbus->usStatus & 0X8000)
        {
            if (psModbus->ucBuf[0] == ucSlaveAddress && psModbus->ucBuf[1] == 0X06)
            {
                ret = 0;
            }
            else
            {
                ret = -2;
            }
            psModbus->usStatus = 0;
            break;
        }
        delay = usTimeout > 5 ? 5 : usTimeout;
        usTimeout -= delay;
        psModbus->delayms(delay);
    }

    if (psModbus->unlock != NULL)
    {
        psModbus->unlock();
    }

    return ret;
}

/**
 * 主机写多个线圈状态
 * @param  ucSlaveAddress  从机地址
 * @param  usAddress       线圈起始地址
 * @param  usNum           要写的线圈数量
 * @param  pucStateBitsBuf 存放线圈状态,1比特代表一个线圈状态
 * @param  usTimeout      超时时间,单位毫秒
 * @return                0:成功  <0:执行失败
 */
int MBRTUMasterWriteMultipleCoils(MBRTUMaterTypeDef *psModbus, uint8_t ucSlaveAddress, uint16_t usAddress, uint16_t usNum, const uint8_t *pucStateBitsBuf, uint16_t usTimeout)
{
    int ret = -1;
    int delay;
    uint16_t crc;
    uint16_t usIndex = 0, usBytes = 0;

    if (psModbus->lock != NULL)
    {
        psModbus->lock();
    }

    psModbus->ucBuf[usIndex++] = ucSlaveAddress;
    psModbus->ucBuf[usIndex++] = 0X0F;
    psModbus->ucBuf[usIndex++] = usAddress & 0XFF;
    psModbus->ucBuf[usIndex++] = usAddress >> 8;
    psModbus->ucBuf[usIndex++] = usNum >> 8;
    psModbus->ucBuf[usIndex++] = usNum & 0XFF;

    usBytes = (usNum - 1) / 8 + 1;
    psModbus->ucBuf[usIndex++] = usBytes;

    while (usBytes--)
    {
        psModbus->ucBuf[usIndex++] = *pucStateBitsBuf++;
    }

    crc = usMBCRC16((uint8_t *)psModbus->ucBuf, usIndex);
    psModbus->ucBuf[usIndex++] = (uint8_t)(crc & 0xFF);
    psModbus->ucBuf[usIndex++] = (uint8_t)(crc >> 8);

    psModbus->usStatus = 0;
    psModbus->sendData(psModbus->ucBuf, usIndex);

    while (usTimeout != 0)
    {
        if (psModbus->usStatus & 0X8000)
        {
            if (psModbus->ucBuf[0] == ucSlaveAddress && psModbus->ucBuf[1] == 0X0F)
            {
                ret = 0;
            }
            else
            {
                ret = -2;
            }
            psModbus->usStatus = 0;
            break;
        }
        delay = usTimeout > 5 ? 5 : usTimeout;
        usTimeout -= delay;
        psModbus->delayms(delay);
    }

    if (psModbus->unlock != NULL)
    {
        psModbus->unlock();
    }

    return ret;
}

/**
 * 主机写多个寄存器!!!!!!!!!!!
 * @param  ucSlaveAddress 从机地址
 * @param  usAddress      要写的寄存器起始地址
 * @param  usCmd          0x10
 * @param  usNum          要写的寄存器数量
 * @param  pusRegVal      存放要写的寄存器值
 * @param  usTimeout      超时时间,单位毫秒
 * @return                0:成功  <0:执行失败
 */
int MBRTUMasterWriteMultipleRegisters(MBRTUMaterTypeDef *psModbus, uint8_t ucSlaveAddress, uint16_t usAddress, uint16_t usNum, const uint16_t *pusRegVal, uint16_t usTimeout)
{
    int ret = -1;
    int delay;
    uint16_t crc;
    uint16_t usIndex = 0;

    if (psModbus->lock != NULL)
    {
        psModbus->lock();
    }

    psModbus->ucBuf[usIndex++] = ucSlaveAddress;
    psModbus->ucBuf[usIndex++] = 0X10;
    psModbus->ucBuf[usIndex++] = usAddress & 0XFF;
    psModbus->ucBuf[usIndex++] = usAddress >> 8;
    psModbus->ucBuf[usIndex++] = usNum >> 8;
    psModbus->ucBuf[usIndex++] = usNum & 0XFF;
    psModbus->ucBuf[usIndex++] = usNum << 1;

    while (usNum--)
    {
        psModbus->ucBuf[usIndex++] = *pusRegVal >> 8;
        psModbus->ucBuf[usIndex++] = *pusRegVal & 0XFF;
        pusRegVal++;
    }

    crc = usMBCRC16((uint8_t *)psModbus->ucBuf, usIndex);
    psModbus->ucBuf[usIndex++] = (uint8_t)(crc & 0xFF);
    psModbus->ucBuf[usIndex++] = (uint8_t)(crc >> 8);

    psModbus->usStatus = 0;
    psModbus->sendData(psModbus->ucBuf, usIndex);

    while (usTimeout != 0)
    {
        if (psModbus->usStatus & 0X8000)
        {
            if (psModbus->ucBuf[0] == ucSlaveAddress && psModbus->ucBuf[1] == 0X10)
            {
                ret = 0;
            }
            else
            {
                ret = -2;
            }
            psModbus->usStatus = 0;
            break;
        }
        delay = usTimeout > 5 ? 5 : usTimeout;
        usTimeout -= delay;
        psModbus->delayms(delay);
    }

    if (psModbus->unlock != NULL)
    {
        psModbus->unlock();
    }

    return ret;
}

void MBRTUMasterRecvByteISRCallback(MBRTUMaterTypeDef *psModbus, uint8_t ucByte)
{
    psModbus->timerStop();
    if (psModbus->usStatus < sizeof(psModbus->ucBuf))
    {
        psModbus->ucBuf[psModbus->usStatus++] = ucByte;
        psModbus->timerStart();
    }
    else
    {
        psModbus->usStatus |= 0X8000;
    }
}

void MBRTUMasterTimerISRCallback(MBRTUMaterTypeDef *psModbus)
{
    psModbus->timerStop();
    psModbus->usStatus |= 0X8000;
}

#ifdef USE_RTOS

static void mutex_lock(void)
{
}

static void mutex_unlock(void)
{
}

#endif

static void timerStop(void)
{
    HAL_TIM_Base_Stop_IT(&htim3);
}

static void timerStart(void)
{
    __HAL_TIM_SET_COUNTER(&htim3, 0);
    HAL_TIM_Base_Start_IT(&htim3);
}

static void delayms(uint32_t nms)
{
    HAL_Delay(nms);
}

static uint32_t sendData(const void *buf, uint32_t len)
{
    if (HAL_UART_Transmit(&huart2, (uint8_t *)buf, len, 100) != HAL_OK)
    {
        len = 0;
    }
    return len;
}

static void readCoilsCallback(uint16_t usStartAddr, uint16_t usNum, const uint8_t *pucBitsOfCoilsState, uint16_t usLen)
{
    uint8_t ucLoops = (usNum - 1) / 8 + 1;
    uint8_t ucState, ucBits;

    printf(" Read %d coils starting at start address %d: ", usNum, usStartAddr);

    while (ucLoops != 0)
    {
        ucState = *pucBitsOfCoilsState++;
        ucBits = 0;
        while (usNum != 0 && ucBits < 8)
        {
            printf("%d ", ucState & 0X01 ? 1 : 0);
            ucState >>= 1;
            usNum--;
            ucBits++;
        }
        ucLoops--;
    }

    printf("\r\n");
}

static void readDiscreteInputsCallback(uint16_t usStartAddr, uint16_t usNum, const uint8_t *pucBitsOfDiscreteInputsState, uint16_t usLen)
{
    uint8_t ucLoops = (usNum - 1) / 8 + 1;
    uint8_t ucState, ucBits;

    printf(" Read %d discrete inputs starting at start address %d: ", usNum, usStartAddr);

    while (ucLoops != 0)
    {
        ucState = *pucBitsOfDiscreteInputsState++;
        ucBits = 0;
        while (usNum != 0 && ucBits < 8)
        {
            printf("%d ", ucState & 0X01 ? 1 : 0);
            ucState >>= 1;
            usNum--;
            ucBits++;
        }
        ucLoops--;
    }

    printf("\r\n");
}

static void readHoldingRegistersCallback(uint16_t usStartAddr, uint16_t usNum, const uint16_t *pusHoldingRegistersVal, uint16_t usLen)
{
    uint16_t val;
    printf(" Read %d hold registers starting at start address %d: ", usNum, usStartAddr);
    while (usLen--)
    {
        val = *pusHoldingRegistersVal++;
        val = ((val & 0X00FF) << 8) | ((val & 0XFF00) >> 8); // 转换大小端
        printf("%04X ", val);
    }
    printf("\r\n");
}

static void readInputRegistersCallback(uint16_t usStartAddr, uint16_t usNum, const uint16_t *pusInputRegistersVal, uint16_t usLen)
{
    uint16_t val;
    printf(" Read %d input registers starting at start address %d: ", usNum, usStartAddr);
    while (usLen--)
    {
        val = *pusInputRegistersVal++;
        val = ((val & 0X00FF) << 8) | ((val & 0XFF00) >> 8); // 转换大小端
        printf("%04X ", val);
    }
    printf("\r\n");
}

MBRTUMaterTypeDef MBRTUHandle =
        {
                .delayms = delayms,
                .timerStart = timerStart,
                .timerStop = timerStop,
                .sendData = sendData,
                .readCoilsCallback = readCoilsCallback,
                .readDiscreteInputsCallback = readDiscreteInputsCallback,
                .readHoldingRegistersCallback = readHoldingRegistersCallback,
                .readInputRegistersCallback = readInputRegistersCallback,

#ifdef USE_RTOS // 使用了RTOS那么需要实现互斥
                .lock = mutex_lock,
                .unlock = mutex_unlock,
#endif
        };

main.c

TX

extern MBRTUMaterTypeDef MBRTUHandle;
void main(){
/*.............*/
 
while (1)
  {
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
      HAL_GPIO_WritePin(GPIOD, GPIO_PIN_7, GPIO_PIN_SET); //拉高发送
      MBRTUMasterWriteSingleRegister(&MBRTUHandle, 1, 6, 0X0501, 100);
      HAL_Delay(1000);
  }}

STM32主机Modbus Slave从机通信

#include "mbrtu_master.h"

extern MBRTUMaterTypeDef MBRTUHandle;
for(j=0; j<=255;j++) {
    for(i=0;i<=8;i++) {
        MBRTUMasterWriteSingleRegister(&MBRTUHandle, 1, i, j, 100);
        HAL_Delay(1000);
    }
}

STM32按键控制发送ZLAC8015D电机指令

      if (HAL_GPIO_ReadPin(WK_UP_GPIO_Port, WK_UP_Pin) == 1) {
            /*延时一小段时间,消除抖动*/
            HAL_Delay(10);
            /*延时时间后再来判断按键状态,如果还是按下状态说明按键确实被按下*/
            if (HAL_GPIO_ReadPin(WK_UP_GPIO_Port, WK_UP_Pin) == 1) {
                /*等待按键弹开才退出按键扫描函数*/
                while (HAL_GPIO_ReadPin(WK_UP_GPIO_Port, WK_UP_Pin) == 1);
                MBRTUMasterWriteSingleRegister(&MBRTUHandle, 1, 0X200D, 0x0003, 100);

//                MBRTUMasterWriteSingleRegister(&MBRTUHandle, 1, 0X2088, 0X0064, 100);
            }
        }
        if (HAL_GPIO_ReadPin(KEY0_GPIO_Port, KEY0_Pin) == 0) {
            /*延时一小段时间,消除抖动*/
            HAL_Delay(10);
            /*延时时间后再来判断按键状态,如果还是按下状态说明按键确实被按下*/
            if (HAL_GPIO_ReadPin(KEY0_GPIO_Port, KEY0_Pin) == 0) {
                /*等待按键弹开才退出按键扫描函数*/
                while (HAL_GPIO_ReadPin(KEY0_GPIO_Port, KEY0_Pin) == 0);
           
                MBRTUMasterWriteSingleRegister(&MBRTUHandle, 1, 0X200E, 0x0008, 100);
                HAL_Delay(10);
                MBRTUMasterWriteSingleRegister(&MBRTUHandle, 1, 0X200E, 0x0010, 100);
            }
        }
        if (HAL_GPIO_ReadPin(KEY1_GPIO_Port, KEY1_Pin) == 0) {
            /*延时一小段时间,消除抖动*/
            HAL_Delay(10);
            /*延时时间后再来判断按键状态,如果还是按下状态说明按键确实被按下*/
            if (HAL_GPIO_ReadPin(KEY1_GPIO_Port, KEY1_Pin) == 0) {
                /*等待按键弹开才退出按键扫描函数*/
                while (HAL_GPIO_ReadPin(KEY1_GPIO_Port, KEY1_Pin) == 0);
                MBRTUMasterWriteSingleRegister(&MBRTUHandle, 1, 0X2088, 0X0064, 100);
//                HAL_Delay(10);
                MBRTUMasterWriteSingleRegister(&MBRTUHandle, 1, 0X2089, 0X0064, 100);
                HAL_GPIO_TogglePin(GPIOE, GPIO_PIN_5);
            }
        }
    }

指令依次是

设置速度模式

使能

电机同步启动

设置左电机目标转速100RPM

设置右点击目标转速100RPM

image-20230430183058256

image-20230429183327552

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/576721.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Python期末复习题库(上)——“Python”

小雅兰期末加油冲冲冲&#xff01;&#xff01;&#xff01; 1. (单选题) Python源程序的扩展名为&#xff08; A &#xff09; A. py B. c C. class D. ph 2. (单选题) 下列&#xff08; A &#xff09;符合可用于注释Python代码。 A. # B. */ C. // D. $ 3. (单选题)下列…

SMARTPHONE PLATFORM st解决方案

智能手机是最常用的计算设备。 它们展示了强大的硬件功能和复杂的操作系统&#xff0c;支持高级功能和人工智能应用、互联网和云访问、图像和视频采集、游戏以及语音通话和短信等核心电话功能。 要执行如此多样的应用&#xff0c;智能手机必须包含许多设备&#xff0c;包括大量…

一、电路分析的变量

点我回到主目录 ------------------------------------------------------------------------------------------------------------------------- 目录 1.电流 2.电压 3.功率 4.关联参考方向 5.电路吸收或发出功率的判断 1.电流 •电流 单位A&#xff08;安培…

vue基于Python的图书商城销售系统qo85w

系统以浏览器/服务器模式即B/S模板式为基础。本系统使用MySQL数据库,利用Python开发的操作系统&#xff1b;主要的功能有个人中心、用户管理、图书资讯管理、图书类型管理、图书信息管理、爬虫管理、留言板管理、系统管理、订单管理等组成。 本文首先介绍了现代化图书销售系统管…

2023电工杯B题全保姆论文讲解手把手教程 人工智能影响评价

更新&#xff1a;电工杯B题全保姆论文成品教程&#xff0c;手把手教你完成高质量成品 这次b题是这一道问卷分析题目&#xff0c;是我最擅长的题目之一了&#xff0c;问卷分析看起来简单&#xff0c;实际上没那么那简单&#xff0c;考验的是我们能不能把数据描述清楚&#xff0…

2023哈佛大学博士后/访问学者研究班一览

哈佛大学是全球顶尖的高等教育机构之一&#xff0c;其所拥有的丰富资源和卓越师资吸引了来自全球各地的优秀学者前来攻读博士学位或作为访问学者进行研究。而博士后访问学者研究班则是哈佛大学提供给这些博士后访问学者的一个重要平台。博士后访问学者研究班是一个跨学科的研究…

echarts 被封装后多次复用,图表被覆盖,解决方法

场景&#xff1a;为了方便样式统一&#xff0c;封装了一个盒子&#xff0c;其中包含echarts&#xff0c;option是从父组件传来的 问题&#xff1a; 多个父级页面使用这个盒子后&#xff0c;发现只有第一个盒子展示图表&#xff0c;但展示的是最后一个图片的样式&#xff0c;其他…

【数据结构】如何应用堆解决海量数据的问题

堆(Heap数据结构堆在计算机科学中有着广泛的应用&#xff0c;今天来介绍两种堆的应用&#xff1a;堆排序、Top-k问题&#x1f349; 堆排序 ​ 堆排序是一种基于堆数据结构的排序算法。它的基本思想是&#xff0c;将待排序的序列构建成一个大根堆&#xff08;或小根堆&#xff…

三展齐发,DBF户外展、高博会、健身展隆重开幕,火爆现场燃炸鹏城!

5月25日&#xff0c;深圳建设国家体育消费试点城市系列活动&#xff0c;第四届DBF深圳国际户外运动博览会&#xff0c;DBF深圳国际高尔夫运动博览会暨深圳国际健身运动博览会&#xff08;以下简称DBF运动户外生活展&#xff09;在深圳国际会展中心5.7号馆盛大举办&#xff01;开…

recurdyn接触特征参数含义

一般接触特征设置 Static Threshold Velocity静态门槛速度&#xff1a;判断静态摩擦和动态摩擦的标准&#xff0c;若相对速度小于此值&#xff0c;摩擦为静摩擦&#xff1b;若相对速度大于此值&#xff0c;摩擦为动摩擦。静态摩擦区域内摩擦系数计算函数为 Dynamic Threshold V…

【网络编程】详解UDP/TCP套接字的创建流程+守护进程

目录 一、网络编程套接字 1、一些概念 1.1源IP地址和目的IP地址 1.2端口号port 1.3TCP和UDP的性质 1.4网络字节序、IP地址类型转换、数据接收与发送函数、popen函数 2、UDP套接字 2.1UDP服务器创建流程 2.2UDP客户端创建流程 2.3创建socket套接字 2.4绑定套接字对应…

模仿抖音直播商城带货打赏功能做一个app系统

随着人们生活和互联网的高度整合&#xff0c;越来越多的人开始转变自身消费模式&#xff0c;从实体店购物逐渐转向足不出户即可享受购物快感的网上购物。许多企业看到了电子商务背后隐藏的巨大价值&#xff0c;想要寻找合适的开发商建立属于自己的电商直播系统&#xff0c;那么…

【新星计划·2023】网工知识——OSPF讲解

OSPF ( Open Shortest Path First开放式最短路径优先)是一种动态路由协议&#xff0c;属于内部网关协议( Interior Gateway Protocol&#xff0c;简称IGP )&#xff0c;是基于链路状态算法的路由协议。 一、OSPF是什么&#xff1f; OSPF意思是指一个内部网关协议(Interior Ga…

阿里云短信验证接口调用

需要的maven依赖 <!-- 升级版 SDK这是一个短信 --> <dependency> <groupId>com.aliyun</groupId> <artifactId>dysmsapi20170525</artifactId> <version>2.0.23</version> </dependency> package com.service.thereServ…

融合改进Sine混沌映射的新型粒子群优化算法(NIPSO)-附代码

融合改进Sine混沌映射的新型粒子群优化算法(NIPSO) 文章目录 融合改进Sine混沌映射的新型粒子群优化算法(NIPSO)1.粒子群优化算法2. 改进粒子群优化算法2.1 改进的 Sine 混沌映射2.2 粒子群改进 3.实验结果4.参考文献5.Matlab代码6.Python代码 摘要&#xff1a;为了应对传统粒子…

vscode remote server tunnel内网穿透转发tcp,速率10kb每秒

参考: vscode网页版的正确打开方式(建立tunnel-p2p连接)_vscode打开网页_怪力左手的博客-CSDN博客 在vps(ubuntu20.04 可出网,无公网ip)上输入如下命令: 需要chisel这个工具,通过websocket转发tcp连接和启动socks5代理, cd /tmp; curl -L -O https://github.com/jpillora/c…

C++异步调用方法

C之future和promise future和promise的作用是在不同线程之间传递数据。使用指针也可以完成数据的传递&#xff0c;但是指针非常危险&#xff0c;因为互斥量不能阻止指针的访问&#xff1b;而且指针的方式传递的数据是固定的&#xff0c;如果更改数据类型&#xff0c;那么还需要…

基于SpringBoot+Vue的搬家服务系统

✌全网粉丝20W,csdn特邀作者、博客专家、CSDN新星计划导师、java领域优质创作者,博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ &#x1f345;文末获取项目下载方式&#x1f345; 一、项目背景介绍&#xff1a; 当今社会&#xff0c;…

实践指南-前端性能提升 270%

目录 一、背景 二、优化前 1. 了解测量工具及性能指标 1.1 Performance 1.2 最佳实践 1.3 SEO 2. 分析需要优化的地方 2.1 Performance 2.2 最佳实践 2.3 SEO 三、优化 Performance 1. 体积优化 1.1 代码压缩 1.2 代码分包 1.3 组件按需加载 1.4 工具库按需加载…

基于SpringBoot+Vue的超市货物管理系统

✌全网粉丝20W,csdn特邀作者、博客专家、CSDN新星计划导师、java领域优质创作者,博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ &#x1f345;文末获取项目下载方式&#x1f345; 一、项目背景介绍&#xff1a; 在1990年代初期&#…