对modbus中的包含下的mbconfig.h进行编辑,把Ascii码配置为0,我们只用用RTU
1.可以新建一个STM32工程,我这里是用的是例程串口232收发的例程,用的是串口2
2.主要先将freemodbus 1.5源码中的modbus文件夹和演示文件夹里的BARE文件复制到工程,然后添加文件到工程
3.配置串口使能函数vMBPortSerialEnable(BOOL xRxEnable,BOOL xTxEnable)
void
vMBPortSerialEnable( BOOL xRxEnable, BOOL xTxEnable )
{
/* If xRXEnable enable serial receive interrupts. If xTxENable enable
* transmitter empty interrupts.
*/
/* If xRXEnable enable serial receive interrupts. If xTxENable enable
* transmitter empty interrupts.
*/
if(TRUE==xRxEnable)
{
USART_ITConfig(RS232_USART, USART_IT_RXNE, ENABLE);
}
else
{
USART_ITConfig(RS232_USART, USART_IT_RXNE, DISABLE);
}
if(TRUE==xTxEnable)
{
USART_ITConfig(RS232_USART, USART_IT_TXE, ENABLE);
}
else
{
USART_ITConfig(RS232_USART, USART_IT_TXE, DISABLE);
}
}
串口初始化函数xMBPortSerialInit(UCHAR ucPORT,ULONG ulBaudRate,UCHAR ucDataBits,eMBParity eParity)
可以将232串口初始化的例程复制到这个函数
BOOL
xMBPortSerialInit( UCHAR ucPORT, ULONG ulBaudRate, UCHAR ucDataBits, eMBParity eParity )
{
(void)ucPORT; //不修改串口
(void)ucDataBits; //不修改数据长度
(void)eParity; //不修改格式
//
GPIO_InitTypeDef GPIO_InitStructure;
USART_InitTypeDef USART_InitStructure;
RCC_AHB1PeriphClockCmd( RS232_USART_RX_GPIO_CLK|RS232_USART_TX_GPIO_CLK, ENABLE);
/*使用UART时钟*/
RCC_APB1PeriphClockCmd(RS232_USART_CLK, ENABLE);
/*连接PXx到USARTx_Tx*/
GPIO_PinAFConfig(RS232_USART_RX_GPIO_PORT,RS232_USART_RX_SOURCE, RS232_USART_RX_AF);
/*连接PXx到USARTx_Rx*/
GPIO_PinAFConfig(RS232_USART_TX_GPIO_PORT,RS232_USART_TX_SOURCE,RS232_USART_TX_AF);
/*配置Tx引脚为复用功能*/
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
GPIO_InitStructure.GPIO_Pin = RS232_USART_TX_PIN ;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(RS232_USART_TX_GPIO_PORT, &GPIO_InitStructure);
/*配置Rx引脚为复用功能*/
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
GPIO_InitStructure.GPIO_Pin = RS232_USART_RX_PIN;
GPIO_Init(RS232_USART_RX_GPIO_PORT, &GPIO_InitStructure);
/* 配置串口RS232_USART模式*/
USART_InitStructure.USART_BaudRate = RS232_USART_BAUDRATE;
USART_InitStructure.USART_WordLength = USART_WordLength_8b;
USART_InitStructure.USART_StopBits = USART_StopBits_1;
USART_InitStructure.USART_Parity = USART_Parity_No ;
USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
USART_Init(RS232_USART, &USART_InitStructure);
USART_ITConfig(RS232_USART, USART_IT_RXNE, ENABLE);
USART_Cmd(RS232_USART, ENABLE);
NVIC_InitTypeDef NVIC_InitStructure;
/* Configure one bit for preemption priority */
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1);
/*中断初始化 */
//设置NVIC优先级分组为Group2:0-3抢占优先级,0-3的响应优先级
NVIC_InitStructure.NVIC_IRQChannel = RS232_USART_IRQ;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
return TRUE;
}
注意:
1- 我这里用的例程串口的.H文件的波特率RS232_USART_BAUDRATE,不是MODBUS初始化函数
eMBInit(MB_RTU,1,1,9600,MB_PAR_NONE);里的波特率9600,而且最后返回TRUE
2- 当波特率大于19200时,usTimerT35_50us固定为35us;否则
usTimerT35_50us =(7UL * 220000UL)/(2UL * ulBaudRate);
4.xMBPortSerialPutByte的作用是将字节数据送到串口。把正常用的串口发送拿过来就可以了
BOOL
xMBPortSerialPutByte( CHAR ucByte )
{
/* Put a byte in the UARTs transmit buffer. This function is called
* by the protocol stack if pxMBFrameCBTransmitterEmpty( ) has been
* called. */
USART_SendData(USART2, ucByte);
while(USART_GetFlagStatus(USART2, USART_FLAG_TC) == RESET){};
return TRUE;
}
5.xMBPortSerialGetByte的作用是从串口接收数据并且把数据传给指定内存。
BOOL
xMBPortSerialGetByte( CHAR * pucByte )
{
/* Return the byte in the UARTs receive buffer. This function is called
* by the protocol stack after pxMBFrameCBByteReceived( ) has been called.
*/
*pucByte = USART_ReceiveData(RS232_USART);
return TRUE;
}
6.配置中断函数
void RS232_USART_IRQHandler(void)
{
if(USART_GetITStatus(RS232_USART, USART_IT_RXNE) == SET)
{
prvvUARTRxISR();
USART_ClearITPendingBit(RS232_USART, USART_IT_RXNE);
}
if(USART_GetITStatus(RS232_USART, USART_IT_TXE) == SET)
{
prvvUARTTxReadyISR();
}
}
注意:需要将这两个函数void prvvUARTTxReadyISR(void);和void prvvUARTRxISR(void); 的静态去掉,并在port.h里声明
才能在中断文件里引用
7.设置定时器初始化,使用的是TIM2
BOOL
xMBPortTimersInit( USHORT usTim1Timerout50us )
{
NVIC_InitTypeDef NVIC_InitStructure;
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
TIM_DeInit(TIM2);
TIM_TimeBaseStructure.TIM_Period = usTim1Timerout50us-1;
TIM_TimeBaseStructure.TIM_Prescaler = (9000 - 1);
TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1;
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure);
TIM_ARRPreloadConfig(TIM2, ENABLE);
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_0);
NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
TIM_ClearITPendingBit(TIM2, TIM_IT_Update);
TIM_ITConfig(TIM2, TIM_IT_Update, DISABLE);
TIM_Cmd(TIM2, DISABLE);
return TRUE;
}
8.vMBPortTimersEnable()使能定时器函数
void
vMBPortTimersEnable( )
{
TIM_ClearITPendingBit(TIM2, TIM_IT_Update);
TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE);
TIM_SetCounter(TIM2, 0);
TIM_Cmd(TIM2, ENABLE);
}
9.vMBPortTimersDisable()定时器失能(关闭)函数
void
vMBPortTimersDisable( )
{
TIM_ClearITPendingBit(TIM2, TIM_IT_Update);
TIM_ITConfig(TIM2, TIM_IT_Update, DISABLE);
TIM_SetCounter(TIM2, 0);
//TIM_Cmd(TIM2, DISABLE);
}
10.配置定时器中断函数
void TIM2_IRQHandler(void)
{
prvvTIMERExpiredISR();
TIM_ClearITPendingBit(TIM2, TIM_IT_Update);
}
注意:需要把static void prvvTIMERExpiredISR(void)函数的静去掉,并且声明在port.h里才能在中断文件里引用
11.新建port.c文件,主要是定义开启/关闭全局中断函数
port.c
#include "stm32f4xx.h"
#include "port.h"
void EnterCriticalSection(void)
{
//看看这个函数的使用位置就知道他们是反过来的,Enter本来意思是进入,但他的位置表明其实应该关闭
//开启全局中断
__disable_irq();
}
void ExitCriticalSection(void)
{
//开启全局中断
__enable_irq();
}
注意:在mbrtu.c文件里
看看这两个函数的位置就知道,按照一般逻辑来说:
一,都是先关闭中断,然后处理数据,再打开终端,
湾并且输入是使能/打开的意思,EXIT是失能/关闭的意思
所以我觉得这两个函数的位置是弄反的,所以我直接在两个函数的具体实现里调换过来。
12.新建一个modbus.c文件,复制demo.c里的四个函数(的的的Modbus通信中,总共有四类的寄存器,开关输入寄存器,线圈寄存器,保持寄存器和输入寄存器)过来,然后自己实现
//这是.h文件的内容
#include "mb.h"
#include "stm32f4xx.h"
#include "bsp_led.h"
#include "relay.h"
#include "modbus.h"
#include "Timer5.h"
#include "mbrtu.h"
#include "main.h"
#include "bsp_SysTick.h"
#include "new.h"
/* -----------------------Slave Defines -------------------------------------*/
#define S_DISCRETE_INPUT_START 1
#define S_DISCRETE_INPUT_NDISCRETES 16
#define S_COIL_START 1
#define S_COIL_NCOILS 104
#define S_REG_INPUT_START 1
#define S_REG_INPUT_NREGS 100
#define S_REG_HOLDING_START 1
#define S_REG_HOLDING_NREGS 100
//从机模式:在保持寄存器中,各个地址对应得功能定义
#define S_HD_RESERVE 0 //保留
#define S_HD_CPU_USAGE_MAJOR 1 //当前CPU利用率的整数位
#define S_HD_CPU_USAGE_MINOR 2 //当前CPU利用率的小数位
//从机模式:在输入寄存器中,各个地址对应的功能定义
#define S_IN_RESERVE 0 //保留
//从机模式:在线圈中,各个地址对应的功能定义
#define S_CO_RESERVE 2 //保留
//从机模式:在离散输入中,各个地址对应的功能定义
#define S_DI_RESERVE 1 //保留
/*------------------------Slave mode use these variables----------------------*/
//Slave mode:DiscreteInputs variables
USHORT usSDiscInStart = S_DISCRETE_INPUT_START;
#if S_DISCRETE_INPUT_NDISCRETES%8
UCHAR ucSDiscInBuf[S_DISCRETE_INPUT_NDISCRETES/8+1];
#else
UCHAR ucSDiscInBuf[S_DISCRETE_INPUT_NDISCRETES/8] ;
#endif
//Slave mode:Coils variables
USHORT usSCoilStart = S_COIL_START;
#if S_COIL_NCOILS%8
UCHAR ucSCoilBuf[S_COIL_NCOILS/8+1] ;
#else
UCHAR ucSCoilBuf[14] ;
#endif
//Slave mode:InputRegister variables
USHORT usSRegInStart = S_REG_INPUT_START;
USHORT usSRegInBuf[100];
//float usSRegInBuf[S_REG_INPUT_NREGS];
//Slave mode:HoldingRegister variables
USHORT usSRegHoldStart = S_REG_HOLDING_START;
USHORT usSRegHoldBuf[250] ;
下面是modbus.c的内容
/*------------------------Slave mode use these variables----------------------*/
//Slave mode:DiscreteInputs variables
USHORT usSDiscInStart = S_DISCRETE_INPUT_START;
#if S_DISCRETE_INPUT_NDISCRETES%8
UCHAR ucSDiscInBuf[S_DISCRETE_INPUT_NDISCRETES/8+1];
#else
UCHAR ucSDiscInBuf[S_DISCRETE_INPUT_NDISCRETES/8] ;
#endif
//Slave mode:Coils variables
USHORT usSCoilStart = S_COIL_START;
#if S_COIL_NCOILS%8
UCHAR ucSCoilBuf[S_COIL_NCOILS/8+1] ;
#else
UCHAR ucSCoilBuf[14] ;
#endif
//Slave mode:InputRegister variables
USHORT usSRegInStart = S_REG_INPUT_START;
USHORT usSRegInBuf[100];
//float usSRegInBuf[S_REG_INPUT_NREGS];
//Slave mode:HoldingRegister variables
USHORT usSRegHoldStart = S_REG_HOLDING_START;
USHORT usSRegHoldBuf[250] ;
eMBErrorCode
eMBRegInputCB( UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNRegs )
{
eMBErrorCode eStatus = MB_ENOERR;
int iRegIndex;
USHORT * pusRegInputBuf;
UCHAR REG_INPUT_START;
UCHAR REG_INPUT_NREGS;
UCHAR usRegInStart;
pusRegInputBuf = usSRegInBuf;
REG_INPUT_START = S_REG_INPUT_START;
REG_INPUT_NREGS = S_REG_INPUT_NREGS;
usRegInStart = usSRegInStart;
if( ( usAddress >= REG_INPUT_START )
&& ( usAddress + usNRegs <= REG_INPUT_START + REG_INPUT_NREGS ) )
{
iRegIndex = ( int )( usAddress - usRegInStart );
while( usNRegs > 0 )
{
*pucRegBuffer++ = ( unsigned char )( pusRegInputBuf[iRegIndex] >> 8 );
*pucRegBuffer++ = ( unsigned char )( pusRegInputBuf[iRegIndex] & 0xFF );
iRegIndex++;
usNRegs--;
}
}
else
{
eStatus = MB_ENOREG;
}
return eStatus;
}
eMBErrorCode
eMBRegHoldingCB( UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNRegs, eMBRegisterMode eMode )
{
eMBErrorCode eStatus = MB_ENOERR;
int iRegIndex;
USHORT * pusRegHoldingBuf;
UCHAR REG_HOLDING_START;
UCHAR REG_HOLDING_NREGS;
UCHAR usRegHoldStart;
pusRegHoldingBuf = usSRegHoldBuf;
REG_HOLDING_START = S_REG_HOLDING_START;
REG_HOLDING_NREGS = S_REG_HOLDING_NREGS;
usRegHoldStart = usSRegHoldStart;
if( ( usAddress >= REG_HOLDING_START ) &&
( usAddress + usNRegs <= REG_HOLDING_START + REG_HOLDING_NREGS ) )
{
iRegIndex = ( int )( usAddress - usRegHoldStart );
switch ( eMode )
{
/* Pass current register values to the protocol stack. */
case MB_REG_READ:
while( usNRegs > 0 )
{
*pucRegBuffer++ = ( unsigned char )( pusRegHoldingBuf[iRegIndex] >> 8 );
*pucRegBuffer++ = ( unsigned char )( pusRegHoldingBuf[iRegIndex] & 0xFF );
iRegIndex++;
usNRegs--;
}
break;
/* Update current register values with new values from the
* protocol stack. */
case MB_REG_WRITE:
while( usNRegs > 0 )
{
pusRegHoldingBuf[iRegIndex] = *pucRegBuffer++ << 8;
pusRegHoldingBuf[iRegIndex] |= *pucRegBuffer++;
iRegIndex++;
usNRegs--;
}
break;
}
}
else
{
eStatus = MB_ENOREG;
}
return eStatus;
}
eMBErrorCode
eMBRegCoilsCB( UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNCoils, eMBRegisterMode eMode )
{
eMBErrorCode eStatus = MB_ENOERR;
int iRegIndex , iRegBitIndex , iNReg;
iNReg = usNCoils / 8 + 1; //Õ¼ÓüĴæÆ÷ÊýÁ¿
if( ( usAddress >= S_COIL_START ) &&
( usAddress + usNCoils <= S_COIL_START + S_COIL_NCOILS ) )
{
iRegIndex = ( int )( usAddress - S_COIL_START ) / 8 ; //ÿ¸ö¼Ä´æÆ÷´æ8¸ö
iRegBitIndex = ( int )( usAddress - usSCoilStart ) % 8 ; //Ïà¶ÔÓڼĴæÆ÷ÄÚ²¿µÄλµØÖ·
switch ( eMode )
{
/* Pass current coil values to the protocol stack. */
case MB_REG_READ:
while( iNReg > 0 )
{
*pucRegBuffer++ = xMBUtilGetBits(&ucSCoilBuf[iRegIndex++] , iRegBitIndex , 8);
iNReg --;
}
pucRegBuffer --;
usNCoils = usNCoils % 8; //ÓàϵÄÏßȦÊý
*pucRegBuffer = *pucRegBuffer <<(8 - usNCoils); //¸ßλ²¹Áã
*pucRegBuffer = *pucRegBuffer >>(8 - usNCoils);
break;
/* Update current coil values with new values from the
* protocol stack. */
case MB_REG_WRITE:
while(iNReg > 1) //×îºóÃæÓàÏÂÀ´µÄÊýµ¥¶ÀËã
{
xMBUtilSetBits(&ucSCoilBuf[iRegIndex++] , iRegBitIndex , 8 , *pucRegBuffer++);
iNReg--;
}
usNCoils = usNCoils % 8; //ÓàϵÄÏßȦÊý
if (usNCoils != 0) //xMBUtilSetBits·½·¨ ÔÚ²Ù×÷λÊýÁ¿Îª0ʱ´æÔÚbug
{
xMBUtilSetBits(&ucSCoilBuf[iRegIndex++], iRegBitIndex, usNCoils,
*pucRegBuffer++);
}
break;
}
}
else
{
eStatus = MB_ENOREG;
}
return eStatus;
}
eMBErrorCode
eMBRegDiscreteCB( UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNDiscrete )
{
eMBErrorCode eStatus = MB_ENOERR;
int iRegIndex , iRegBitIndex , iNReg;
UCHAR * pucDiscreteInputBuf;
UCHAR DISCRETE_INPUT_START;
UCHAR DISCRETE_INPUT_NDISCRETES;
UCHAR usDiscreteInputStart;
iNReg = usNDiscrete / 8 + 1; //Õ¼ÓüĴæÆ÷ÊýÁ¿
pucDiscreteInputBuf = ucSDiscInBuf;
DISCRETE_INPUT_START = S_DISCRETE_INPUT_START;
DISCRETE_INPUT_NDISCRETES = S_DISCRETE_INPUT_NDISCRETES;
usDiscreteInputStart = usSDiscInStart;
pucDiscreteInputBuf[0]=0x07;
if( ( usAddress >= DISCRETE_INPUT_START )
&& ( usAddress + usNDiscrete <= DISCRETE_INPUT_START + DISCRETE_INPUT_NDISCRETES ) )
{
iRegIndex = ( int )( usAddress - usDiscreteInputStart ) / 8 ; //ÿ¸ö¼Ä´æÆ÷´æ8¸ö
iRegBitIndex = ( int )( usAddress - usDiscreteInputStart ) % 8 ; //Ïà¶ÔÓڼĴæÆ÷ÄÚ²¿µÄλµØÖ·
while( iNReg > 0 )
{
*pucRegBuffer++ = xMBUtilGetBits(&pucDiscreteInputBuf[iRegIndex++] , iRegBitIndex , 8);
iNReg --;
}
pucRegBuffer --;
usNDiscrete = usNDiscrete % 8; //ÓàϵÄÏßȦÊý
*pucRegBuffer = *pucRegBuffer <<(8 - usNDiscrete); //¸ßλ²¹Áã
*pucRegBuffer = *pucRegBuffer >>(8 - usNDiscrete);
}
else
{
eStatus = MB_ENOREG;
}
return eStatus;
}
13.在主函数里初始化,然后使用输入寄存器第一和第二个寄存器来测试
int main(void)
{
eMBInit(MB_RTU, 1, 1, 9600, MB_PAR_NONE);
eMBEnable();
test_data1.d=11;
while(1)
{
( void)eMBPoll();
test_data1.d =test_data1.d +0.001;
usSRegInBuf[0]=test_data1.value[3];
usSRegInBuf[0]=(usSRegInBuf[0]<<8)+test_data1.value[2];
usSRegInBuf[1]=test_data1.value[1];
usSRegInBuf[1]=(usSRegInBuf[1]<<8)+test_data1.value[0];
}
}
注意:TEST_DATA1是联体数,不懂得可以百度
移植好的代码链接:https://download.csdn.net/download/qq_22079023/10840362
我不知道怎么直接把代码放到这里点击就可以下载,有会的希望能告知一下!
如果对于freemodbus不了解的可以看看这个https://blog.csdn.net/liukais/article/details/73385328