1.创建工程
2.将FreeModbus源码,拷贝到工程目录
3.将FreeModbus文件添加进工程
添加好之后,编译出现错误
4.移植底层接口
先看第一个错误,缺少port.h
借鉴AVR架构的程序,将demo里面AVR中的port文件夹,拷贝到工程中
进入port文件夹,删除重复文件,mbcrc.c
添加好之后,重新编译,出现错误,port.h中包含了很多iar相关代码
将这些代码删除或修改
重新编译,错误少了很多,剩下的错误是,portserial和porttimer中包含avr相关代码
将portserial和porttimer中,串口和定时器的驱动,改成STM32架构的代码
#include "port.h"
/* ----------------------- Modbus includes ----------------------------------*/
#include "mb.h"
#include "mbport.h"
/* GPIO配置 */
static void gpio_config(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
/* 使能GPIOA时钟 */
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
/* 设置PA2为复用推挽输出 */
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_Init(GPIOA, &GPIO_InitStructure);
/* 设置PA3为浮空输入 */
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_Init(GPIOA, &GPIO_InitStructure);
}
/* 中断配置 */
static void nvic_config(void)
{
NVIC_InitTypeDef NVIC_InitStructure;
/* 设置串口1中断的先占优先级为2,从占优先级为2 */
NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
}
/* 串口配置 */
static void usart_config(uint32_t baudRate, uint16_t wordLength, uint16_t parity)
{
USART_InitTypeDef USART_InitStruct;
/* 使能GPIOA时钟和串口时钟 */
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);
/* 设置串口波特率115200、数据位8bit、停止位1bit、校验位无、无硬件流、接收发送 */
if(wordLength == 9)
USART_InitStruct.USART_WordLength = USART_WordLength_9b;
else
USART_InitStruct.USART_WordLength = USART_WordLength_8b;
if(MB_PAR_NONE != parity)
USART_InitStruct.USART_StopBits = USART_StopBits_1;
else
USART_InitStruct.USART_StopBits = USART_StopBits_2;
if(MB_PAR_NONE == parity)
USART_InitStruct.USART_Parity = USART_Parity_No;
else if(MB_PAR_EVEN == parity)
USART_InitStruct.USART_Parity = USART_Parity_Even;
else
USART_InitStruct.USART_Parity = USART_Parity_Odd;
USART_InitStruct.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
USART_InitStruct.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
USART_Init(USART1, &USART_InitStruct);
}
void vMBPortSerialEnable(BOOL xRxEnable, BOOL xTxEnable)
{
if(xRxEnable == FALSE && xTxEnable == FALSE)
USART_Cmd(USART1, DISABLE);
else
USART_Cmd(USART1, ENABLE);
if(xRxEnable == TRUE)
{
USART_ClearITPendingBit(USART1, USART_IT_RXNE);
USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);
}
else
USART_ITConfig(USART1, USART_IT_RXNE, DISABLE);
if(xTxEnable == TRUE)
{
USART_ClearITPendingBit(USART1, USART_IT_TC);
USART_ITConfig(USART1, USART_IT_TC, ENABLE);
pxMBFrameCBTransmitterEmpty();
}
else
USART_ITConfig(USART1, USART_IT_TC, DISABLE);
}
BOOL xMBPortSerialInit(UCHAR ucPORT, ULONG ulBaudRate, UCHAR ucDataBits, eMBParity eParity)
{
(void)ucPORT;
/* 串口配置 */
usart_config(ulBaudRate, ucDataBits, eParity);
/* GPIO配置 */
gpio_config();
/* 中断配置 */
nvic_config();
vMBPortSerialEnable(FALSE, FALSE);
return TRUE;
}
BOOL xMBPortSerialPutByte(CHAR ucByte)
{
USART_SendData(USART1, ucByte);
return TRUE;
}
BOOL xMBPortSerialGetByte(CHAR *pucByte)
{
*pucByte = USART_ReceiveData(USART1);
return TRUE;
}
void USART1_IRQHandler(void)
{
if(USART_GetITStatus(USART1, USART_IT_ORE) != RESET)
{
USART_ClearITPendingBit(USART1, USART_IT_ORE);
pxMBFrameCBByteReceived();
}
if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET)
{
USART_ClearITPendingBit(USART1, USART_IT_RXNE);
pxMBFrameCBByteReceived();
}
if(USART_GetITStatus(USART1, USART_IT_TC) != RESET)
{
USART_ClearITPendingBit(USART1, USART_IT_TC);
pxMBFrameCBTransmitterEmpty();
}
}
/* ----------------------- Platform includes --------------------------------*/
#include "port.h"
/* ----------------------- Modbus includes ----------------------------------*/
#include "mb.h"
#include "mbport.h"
/* 功能: 定时器配置
参数: period 自动重载值
返回值: 无
*/
static void timer_config(uint16_t period)
{
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
/* 允许TIM3的时钟 */
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);
/* 将定时器3寄存器设为初始值 */
TIM_DeInit(TIM3);
/* 设置定时器3由内部时钟 */
TIM_InternalClockConfig(TIM3);
/* 预分频值 */
TIM_TimeBaseStructure.TIM_Prescaler = 3600 - 1;
/* 时钟分割 */
TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1;
/* 向上计数 */
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
/* 自动重载值 */
TIM_TimeBaseStructure.TIM_Period = period - 1;
/* 初始化定时器3 */
TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure);
/* 清除溢出中断标志 */
TIM_ClearFlag(TIM3, TIM_FLAG_Update);
/* 禁止ARR预装载缓冲器 */
TIM_ARRPreloadConfig(TIM3, DISABLE);
}
/* 功能: 中断配置
参数: 无
返回值: 无
*/
static void nvic_config(void)
{
NVIC_InitTypeDef NVIC_InitStructure;
/* 选择TIM3的中断通道 */
NVIC_InitStructure.NVIC_IRQChannel = TIM3_IRQn;
/* 抢占式中断优先级设置为2 */
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2;
/* 响应式中断优先级设置为2 */
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2;
/* 使能中断 */
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
/* 中断初始化 */
NVIC_Init(&NVIC_InitStructure);
}
BOOL xMBPortTimersInit(USHORT usTim1Timerout50us)
{
/* 设置定时器定时时间 */
timer_config(usTim1Timerout50us);
/* 中断配置 */
nvic_config();
return TRUE;
}
void vMBPortTimersEnable(void)
{
/* 清空定时器3计数器 */
TIM_SetCounter(TIM3, 0);
/* 使能定时器3 */
TIM_Cmd(TIM3, ENABLE);
/* 使能TIM3的中断 */
TIM_ITConfig(TIM3, TIM_IT_Update, ENABLE);
}
void vMBPortTimersDisable(void)
{
/* 禁止定时器3 */
TIM_Cmd(TIM3, DISABLE);
/* 关闭TIM3的中断 */
TIM_ITConfig(TIM3, TIM_IT_Update, DISABLE);
}
/* 定时器3中断向量 */
void TIM3_IRQHandler(void)
{
/* 定时器3溢出标志位 */
if(TIM_GetITStatus(TIM3, TIM_IT_Update) != RESET)
{
/* 定时器3清除溢出标志位 */
TIM_ClearITPendingBit(TIM3 , TIM_FLAG_Update);
/* 关闭TIM3的中断 */
TIM_ITConfig(TIM3, TIM_IT_Update, DISABLE);
/* 关闭定时器3 */
TIM_Cmd(TIM3, DISABLE);
(void)pxMBPortCBTimerExpired();
}
}
重新编译,驱动相关的错误,已经不存在。
报告eMBRegCoilsCB、eMBRegDiscreteCB、eMBRegHoldingCB和eMBRegInputCB没有定义。
这四个接口是协议栈预留给用户自己去实现的,分别创建user_mb_app.h和user_mb_app.c两个文件
#ifndef _USER_MB_APP_H_
#define _USER_MB_APP_H_
/* ----------------------- Modbus includes ----------------------------------*/
#include "mb.h"
#include "mbconfig.h"
#include "mbframe.h"
#include "mbutils.h"
/* -----------------------Slave Defines -------------------------------------*/
#define S_DISCRETE_INPUT_START 0
#define S_DISCRETE_INPUT_NDISCRETES 16
#define S_COIL_START 0
#define S_COIL_NCOILS 64
#define S_REG_INPUT_START 0
#define S_REG_INPUT_NREGS 100
#define S_REG_HOLDING_START 0
#define S_REG_HOLDING_NREGS 100
/* salve mode: holding register's all address */
#define S_HD_RESERVE 0
#define S_HD_CPU_USAGE_MAJOR 1
#define S_HD_CPU_USAGE_MINOR 2
/* salve mode: input register's all address */
#define S_IN_RESERVE 0
/* salve mode: coil's all address */
#define S_CO_RESERVE 0
/* salve mode: discrete's all address */
#define S_DI_RESERVE 0
/* -----------------------Master Defines -------------------------------------*/
#define M_DISCRETE_INPUT_START 0
#define M_DISCRETE_INPUT_NDISCRETES 16
#define M_COIL_START 0
#define M_COIL_NCOILS 64
#define M_REG_INPUT_START 0
#define M_REG_INPUT_NREGS 100
#define M_REG_HOLDING_START 0
#define M_REG_HOLDING_NREGS 100
/* master mode: holding register's all address */
#define M_HD_RESERVE 0
/* master mode: input register's all address */
#define M_IN_RESERVE 0
/* master mode: coil's all address */
#define M_CO_RESERVE 0
/* master mode: discrete's all address */
#define M_DI_RESERVE 0
#endif
/*
* FreeModbus Libary: user callback functions and buffer define in slave mode
* Copyright (C) 2013 Armink
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
* File: $Id: user_mb_app.c,v 1.60 2013/11/23 11:49:05 Armink $
*/
#include "user_mb_app.h"
/*------------------------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[S_COIL_NCOILS/8] ;
#endif
//Slave mode:InputRegister variables
USHORT usSRegInStart = S_REG_INPUT_START;
USHORT usSRegInBuf[S_REG_INPUT_NREGS] ;
//Slave mode:HoldingRegister variables
USHORT usSRegHoldStart = S_REG_HOLDING_START;
USHORT usSRegHoldBuf[S_REG_HOLDING_NREGS] ;
/**
* Modbus slave input register callback function.
*
* @param pucRegBuffer input register buffer
* @param usAddress input register address
* @param usNRegs input register number
*
* @return result
*/
eMBErrorCode eMBRegInputCB(UCHAR *pucRegBuffer, USHORT usAddress, USHORT usNRegs)
{
eMBErrorCode eStatus = MB_ENOERR;
USHORT iRegIndex;
USHORT * pusRegInputBuf;
USHORT REG_INPUT_START;
USHORT REG_INPUT_NREGS;
USHORT usRegInStart;
pusRegInputBuf = usSRegInBuf;
REG_INPUT_START = S_REG_INPUT_START;
REG_INPUT_NREGS = S_REG_INPUT_NREGS;
usRegInStart = usSRegInStart;
/* it already plus one in modbus function method. */
usAddress--;
if ((usAddress >= REG_INPUT_START)
&& (usAddress + usNRegs <= REG_INPUT_START + REG_INPUT_NREGS))
{
iRegIndex = usAddress - usRegInStart;
while (usNRegs > 0)
{
*pucRegBuffer++ = (UCHAR) (pusRegInputBuf[iRegIndex] >> 8);
*pucRegBuffer++ = (UCHAR) (pusRegInputBuf[iRegIndex] & 0xFF);
iRegIndex++;
usNRegs--;
}
}
else
{
eStatus = MB_ENOREG;
}
return eStatus;
}
/**
* Modbus slave holding register callback function.
*
* @param pucRegBuffer holding register buffer
* @param usAddress holding register address
* @param usNRegs holding register number
* @param eMode read or write
*
* @return result
*/
eMBErrorCode eMBRegHoldingCB(UCHAR *pucRegBuffer, USHORT usAddress, USHORT usNRegs, eMBRegisterMode eMode)
{
eMBErrorCode eStatus = MB_ENOERR;
USHORT iRegIndex;
USHORT * pusRegHoldingBuf;
USHORT REG_HOLDING_START;
USHORT REG_HOLDING_NREGS;
USHORT usRegHoldStart;
pusRegHoldingBuf = usSRegHoldBuf;
REG_HOLDING_START = S_REG_HOLDING_START;
REG_HOLDING_NREGS = S_REG_HOLDING_NREGS;
usRegHoldStart = usSRegHoldStart;
/* it already plus one in modbus function method. */
usAddress--;
if ((usAddress >= REG_HOLDING_START)
&& (usAddress + usNRegs <= REG_HOLDING_START + REG_HOLDING_NREGS))
{
iRegIndex = usAddress - usRegHoldStart;
switch (eMode)
{
/* read current register values from the protocol stack. */
case MB_REG_READ:
while (usNRegs > 0)
{
*pucRegBuffer++ = (UCHAR) (pusRegHoldingBuf[iRegIndex] >> 8);
*pucRegBuffer++ = (UCHAR) (pusRegHoldingBuf[iRegIndex] & 0xFF);
iRegIndex++;
usNRegs--;
}
break;
/* write 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;
}
/**
* Modbus slave coils callback function.
*
* @param pucRegBuffer coils buffer
* @param usAddress coils address
* @param usNCoils coils number
* @param eMode read or write
*
* @return result
*/
eMBErrorCode eMBRegCoilsCB(UCHAR *pucRegBuffer, USHORT usAddress, USHORT usNCoils, eMBRegisterMode eMode)
{
eMBErrorCode eStatus = MB_ENOERR;
USHORT iRegIndex , iRegBitIndex , iNReg;
UCHAR * pucCoilBuf;
USHORT COIL_START;
USHORT COIL_NCOILS;
USHORT usCoilStart;
iNReg = usNCoils / 8 + 1;
pucCoilBuf = ucSCoilBuf;
COIL_START = S_COIL_START;
COIL_NCOILS = S_COIL_NCOILS;
usCoilStart = usSCoilStart;
/* it already plus one in modbus function method. */
usAddress--;
if( ( usAddress >= COIL_START ) &&
( usAddress + usNCoils <= COIL_START + COIL_NCOILS ) )
{
iRegIndex = (USHORT) (usAddress - usCoilStart) / 8;
iRegBitIndex = (USHORT) (usAddress - usCoilStart) % 8;
switch ( eMode )
{
/* read current coil values from the protocol stack. */
case MB_REG_READ:
while (iNReg > 0)
{
*pucRegBuffer++ = xMBUtilGetBits(&pucCoilBuf[iRegIndex++],
iRegBitIndex, 8);
iNReg--;
}
pucRegBuffer--;
/* last coils */
usNCoils = usNCoils % 8;
/* filling zero to high bit */
*pucRegBuffer = *pucRegBuffer << (8 - usNCoils);
*pucRegBuffer = *pucRegBuffer >> (8 - usNCoils);
break;
/* write current coil values with new values from the protocol stack. */
case MB_REG_WRITE:
while (iNReg > 1)
{
xMBUtilSetBits(&pucCoilBuf[iRegIndex++], iRegBitIndex, 8,
*pucRegBuffer++);
iNReg--;
}
/* last coils */
usNCoils = usNCoils % 8;
/* xMBUtilSetBits has bug when ucNBits is zero */
if (usNCoils != 0)
{
xMBUtilSetBits(&pucCoilBuf[iRegIndex++], iRegBitIndex, usNCoils,
*pucRegBuffer++);
}
break;
}
}
else
{
eStatus = MB_ENOREG;
}
return eStatus;
}
/**
* Modbus slave discrete callback function.
*
* @param pucRegBuffer discrete buffer
* @param usAddress discrete address
* @param usNDiscrete discrete number
*
* @return result
*/
eMBErrorCode eMBRegDiscreteCB(UCHAR *pucRegBuffer, USHORT usAddress, USHORT usNDiscrete)
{
eMBErrorCode eStatus = MB_ENOERR;
USHORT iRegIndex , iRegBitIndex , iNReg;
UCHAR * pucDiscreteInputBuf;
USHORT DISCRETE_INPUT_START;
USHORT DISCRETE_INPUT_NDISCRETES;
USHORT usDiscreteInputStart;
iNReg = usNDiscrete / 8 + 1;
pucDiscreteInputBuf = ucSDiscInBuf;
DISCRETE_INPUT_START = S_DISCRETE_INPUT_START;
DISCRETE_INPUT_NDISCRETES = S_DISCRETE_INPUT_NDISCRETES;
usDiscreteInputStart = usSDiscInStart;
/* it already plus one in modbus function method. */
usAddress--;
if ((usAddress >= DISCRETE_INPUT_START)
&& (usAddress + usNDiscrete <= DISCRETE_INPUT_START + DISCRETE_INPUT_NDISCRETES))
{
iRegIndex = (USHORT) (usAddress - usDiscreteInputStart) / 8;
iRegBitIndex = (USHORT) (usAddress - usDiscreteInputStart) % 8;
while (iNReg > 0)
{
*pucRegBuffer++ = xMBUtilGetBits(&pucDiscreteInputBuf[iRegIndex++],
iRegBitIndex, 8);
iNReg--;
}
pucRegBuffer--;
/* last discrete */
usNDiscrete = usNDiscrete % 8;
/* filling zero to high bit */
*pucRegBuffer = *pucRegBuffer << (8 - usNDiscrete);
*pucRegBuffer = *pucRegBuffer >> (8 - usNDiscrete);
}
else
{
eStatus = MB_ENOREG;
}
return eStatus;
}
重新编译,0错误,0警告
5.添加接口
分别创建rcc.h、rcc.c、nvic.h、nvic.c文件
/**
***********************************
* 文件名:rcc.h
* 作者: stone
* 版本: V0.1
* 日期: 2018-3-29
* 描述: 配置时钟源
***********************************
*/
#ifndef __RCC_H_
#define __RCC_H_
/* 功能: RCC时钟配置
参数: 无
返回值:无
*/
void rcc_config(void);
#endif
/**
***********************************
* 文件名: rcc.c
* 作者: stone
* 版本: V0.1
* 日期: 2018-3-29
* 描述: 配置时钟源
***********************************
*/
#include "stm32f10x.h"
#include "stm32f10x_flash.h"
#include "rcc.h"
/* 功能: RCC时钟配置
参数: 无
返回值:无
*/
void rcc_config(void)
{
ErrorStatus HSEStartUpStatus;
/* RCC寄存器设置为默认配置 */
RCC_DeInit();
/* 打开外部高速时钟 */
RCC_HSEConfig(RCC_HSE_ON);
/* 等待外部高速时钟稳定 */
HSEStartUpStatus = RCC_WaitForHSEStartUp();
if(HSEStartUpStatus == SUCCESS)
{
/* 设置HCLK = SYSCLK */
RCC_HCLKConfig(RCC_SYSCLK_Div1);
/* 设置PCLK2 = HCLK */
RCC_PCLK2Config(RCC_HCLK_Div1);
/* 设置PCLK1 = HCLK / 2 */
RCC_PCLK1Config(RCC_HCLK_Div2);
/* 设置FLASH代码延时 */
FLASH_SetLatency(FLASH_Latency_2);
/* 使能预取址缓存 */
FLASH_PrefetchBufferCmd(FLASH_PrefetchBuffer_Enable);
/* 设置PLL时钟源为HSE倍频9 72MHz */
RCC_PLLConfig(RCC_PLLSource_HSE_Div1, RCC_PLLMul_9);
/* 使能PLL */
RCC_PLLCmd(ENABLE);
/* 等待PLL稳定 */
while(RCC_GetFlagStatus(RCC_FLAG_PLLRDY) == RESET);
/* 设置PLL为系统时钟源 */
RCC_SYSCLKConfig(RCC_SYSCLKSource_PLLCLK);
/* 等待系统时钟源切换到PLL */
while(RCC_GetSYSCLKSource() != 0x08);
}
}
/**
***********************************
* 文件名: nvic.h
* 作者: stone
* 版本: V0.1
* 日期: 2018-3-29
* 描述: 设置中断控制器
***********************************
*/
#ifndef __NVIC_H_
#define __NVIC_H_
/* 功能: 中断嵌套控制器配置
参数: 无
返回值:无
*/
void nvic_config(void);
#endif
/**
***********************************
* 文件名: nvic.c
* 作者: stone
* 版本: V0.1
* 日期: 2018-3-29
* 描述: 设置中断控制器
***********************************
*/
#include "stm32f10x.h"
#include "nvic.h"
/* 功能: 中断嵌套控制器配置
参数: 无
返回值:无
*/
void nvic_config(void)
{
/* 选择中断分组2 */
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
}
8.协议栈移植工作完成。
#include "stm32f10x.h"
#include "rcc.h"
#include "nvic.h"
#include "mb.h"
int main(void)
{
/* RCC时钟配置 */
rcc_config();
/* 中断嵌套控制器配置 */
nvic_config();
/* 设置从节点ID */
eMBSetSlaveID(1, TRUE, 0, 0);
/* 初始化Modbus */
eMBInit(MB_RTU, 1, 1, 115200, MB_PAR_NONE);
/* 使能Modbus */
eMBEnable();
/* 主循环 */
while(1)
{
/* 轮询Modbus */
eMBPoll();
}
}