Modbus是一项工业上经常用到的通讯协议,而freemodbus是一款开源的从机协议栈。关于它的移植网上已经有了很多的文章,但是对于移植到PIC单片机,少之又少。本文将重介绍freemodbus在PIC18F47Q84单片机的移植。

本次移植使用的是PIC18F47Q84开发板,编译环境 MPLAB X IDE v5.50

想现解PIC18F47Q84开发板更多信息,请点击下面 PIC18F47Q84开发板

PIC18F47Q84开发板

1、FreeModbus 源码的获取

freemodbus源码GitHub链接:

GitHub - cwalter-at/freemodbus: BSD licensed MODBUS RTU/ASCII and TCP slave

2、FreeModbus 移植到PIC18F47Q84

2.1 准备基础工程

我们先准备一个基础工程,所以我们选择第十五章RS485通信实验做为基础工程。

将其拷贝一份,重命名为 FreeModbus_RS485,然后,我们将下载的FreeModbus文件里的modbus这个文件夹全部复制到我们的工程路径,然后将demo文件里的BARE文件夹也复制到我们的工程路径,如下,

(1)、右击  sourse file, 选择 New Logical Folder

新建4个文件夹,分别命名为Driver,FreeModbus_app,FreeModbus_core,FreeModbus_port, 分别放置文件如下:

 

 

(2)、相同的方法,添加相应的头文件

右击 Heads Files 选择 New Logical Folder,同样的,新建4个文件夹,分别命名为Driver,FreeModbus_app,FreeModbus_core,FreeModbus_port, 分别放置和源文件对应的头文件。

 

(3)、移植后,我们编译会发现有很多问题,都是头文件没有找到。

MPLAB X IDE要添加的头文件,都是要指定相对应的路径才可以,以portserial.c为例,

其它文件同理,如mbfuncdisc.c

屏蔽掉的是原文件的写法,添加上路径是我们MPLAB X IDE要求的写法。

把头文件路径一一添加完成后,再次编译,成功!!!

2.2、修改FreeModbus 中portserial.c 文件

前面添加进来的FreeModbus 源码是没有任何串口和定时器的驱动,接下来我们需要对FreeModbus 的portserial.c 文件添加Modbus 串口发送中断和接收

中断使能函数。

portserial.c 修改后的具体代码如下:(红色标记为添加的代码)

#include <xc.h>

#include "port.h"

#include "../../../Driver/device_config.h"

/* ----------------------- Modbus includes ----------------------------------*/

#include "../../include/mb.h"

#include "../../include/mbport.h"

#define F_OSC 64000000        // 64MHz   #define F_OSC1 64000000

#define UART2_BAUD_RATE          9600

#define UART2_BAUD_CALC(UART2_BAUD_RATE,F_OSC) \

( ( F_OSC ) / ( ( UART2_BAUD_RATE ) * 4UL ) - 1 )

/* ----------------------- static functions ---------------------------------*/

static void prvvUARTTxReadyISR( void );

static void prvvUARTRxISR( void );

/* ----------------------- Start implementation -----------------------------*/

void vMBPortSerialEnable( BOOL xRxEnable, BOOL xTxEnable )

{

//MCU 串口接收中断使能

if(xRxEnable==TRUE)

{

//使能接收和接收中断

LATCbits.LATC1 = 0;     // RS485驱动芯片,接收使能

PIR8bits.U2RXIF = 0;

PIE8bits.U2RXIE = 1;    // enable receive interrupt

}

else

{

//禁止接收和接收中断

LATCbits.LATC1 = 1;     //RS485驱动芯片,发送使能

PIR8bits.U2RXIF = 0;

PIE8bits.U2RXIE = 0;    // enable receive interrupt

}

//MCU 串口发送中断使能

if(xTxEnable==TRUE)

{

PIR8bits.U2TXIF = 0;

PIE8bits.U2TXIE = 1;    //使能发送中断

}

else

{

PIR8bits.U2TXIF = 0;

PIE8bits.U2TXIE = 0;    //禁止发送中断

}

}

BOOL

xMBPortSerialInit( UCHAR ucPORT, ULONG ulBaudRate, UCHAR ucDataBits, eMBParity eParity )

{

(void)ucPORT;       //用串口2,不修改串口

(void)ucDataBits;   //不修改数据位长度

// 模式控制寄存器,比如 MAX , DALI, LIN

U2P1L = 0x00;           // P1L 0;

U2P1H = 0x00;           // P1H 0;

U2P2L = 0x00;           // P2L 0;

U2P2H = 0x00;           // P2H 0;

U2P3L = 0x00;           // P3L 0;

U2P3H = 0x00;           // P3H 0;

U2CON0 = 0xB0;

U2CON1 = 0x80;

U2CON2 = 0x00;

U2BRG = UART2_BAUD_CALC(ulBaudRate,F_OSC);

U2FIFO = 0x00;

U2UIR = 0x00;

U2ERRIR = 0x00;

U2ERRIE = 0x00;

switch(eParity)

{

case MB_PAR_EVEN:       // 奇偶校验

U2CON0bits.MODE = 0x3;

break;

case MB_PAR_ODD:        // 奇偶校验

U2CON0bits.MODE = 0x2;

break;

case MB_PAR_NONE:       // 无奇偶校验

U2CON0bits.MODE = 0x0;

break;

default:break;

}

// enable error interrupt

PIE8bits.U2EIE = 1;

vMBPortSerialEnable(TRUE, FALSE );

return TRUE;

}

BOOL

xMBPortSerialPutByte( CHAR ucByte )

{

while(!U2FIFObits.TXBE);    // 发送一个数据

U2TXB = ucByte;

return TRUE;

}

BOOL

xMBPortSerialGetByte( CHAR * pucByte )

{

*pucByte = U2RXB;               // 接收一个数据

return TRUE;

}

/* Create an interrupt handler for the transmit buffer empty interrupt

* (or an equivalent) for your target processor. This function should then

* call pxMBFrameCBTransmitterEmpty( ) which tells the protocol stack that

* a new character can be sent. The protocol stack will then call

* xMBPortSerialPutByte( ) to send the character.

*/

static void prvvUARTTxReadyISR( void )

{

LATCbits.LATC1 = 1;        //MAX485 使能发送

pxMBFrameCBTransmitterEmpty( );

LATCbits.LATC1 = 0;

}

/* Create an interrupt handler for the receive interrupt for your target

* processor. This function should then call pxMBFrameCBByteReceived( ). The

* protocol stack will then call xMBPortSerialGetByte( ) to retrieve the

* character.

*/

static void prvvUARTRxISR( void )

{

pxMBFrameCBByteReceived(  );

}

/******************************************************************************

Name        :   INTERRUPT_InterruptManagerHigh()

Input       :

Output      :

description :   高优先级中断

******************************************************************************/

void __interrupt() INTERRUPT_InterruptManagerHigh (void)

{

if(PIE8bits.U2RXIE && PIR8bits.U2RXIF)

{

PIR8bits.U2RXIF = 0;    //清中断标志

prvvUARTRxISR();        //接受中断

}

else if(PIE8bits.U2TXIE && PIR8bits.U2TXIF)

{

PIR8bits.U2TXIF = 0;    //清中断标志位

prvvUARTTxReadyISR();   //发送完成中断

}

else if(PIE8bits.U2EIE == 1 && PIR8bits.U2EIF == 1)

{

PIR8bits.U2EIF = 0;

LATEbits.LATE0 = 1;

U2ERRIR = 0;  // To clear the interrupt condition, all bits in the UxERRIR register must be cleared

}

}

首先在portserial.c 文件开头添加#include <xc.h>语句,因为该头文件内已经包含了 PIC18F47Q84 对应的头文件,如果不添加的话,那么再添加串口等驱动时会提示错误。

另外,要添加#include "../../../Driver/device_config.h" 头文件, 因为在这个头文件定义 #define _XTAL_FREQ 64000000 ,这个在调用__delay_ms(1);时会用到,否则编译会报错。

2.3、修改FreeModbus 中porttimer.c 文件

接下来需要修改的是porttimer.c 文件,需要补充的就是Modbus 定时器初始化函数、Modbus 定时器使能和失能函数,以及Modbus 定时器中断函数。这个也是非常简单的。具体代码如下:(红色标记为添加代码)

/* ----------------------- Platform includes --------------------------------*/

#include <xc.h>

#include "port.h"

/* ----------------------- Modbus includes ----------------------------------*/

#include "../../include/mb.h"

#include "../../include/mbport.h"

static void prvvTIMERExpiredISR( void );

/* ----------------------- Defines ------------------------------------------*/

#define MB_TIMER_PRESCALER      ( 6400UL )

#define F_CPU                  64000000ul

#define MB_TIMER_TICKS          ( F_CPU / MB_TIMER_PRESCALER )

#define MB_50US_TICKS           ( 1000UL )

/* ----------------------- Static variables ---------------------------------*/

static USHORT   usTimerOCRADelta;

//static USHORT   usTimerOCRBDelta;

/* ----------------------- Start implementation -----------------------------*/

BOOL

xMBPortTimersInit( USHORT usTim1Timerout50us )

{

usTimerOCRADelta = (uint16_t)(65536ul - ( MB_TIMER_TICKS * usTim1Timerout50us ) / ( MB_50US_TICKS ));

// 50us interrupt

T0CON1 = 0x43;

T0CON0 = 0x90;

TMR0 = 0x00;

T0CON0bits.T0EN = 0;    // Clear Interrupt flag before enabling the interrupt

PIE3bits.TMR0IE = 1;   // Enabling TMR0 interrupt.

return TRUE;

}

void vMBPortTimersEnable()

{

/* Enable the timer with the timeout passed to xMBPortTimersInit( ) */

if( usTimerOCRADelta > 0 )

{

TMR0H = (unsigned char)(usTimerOCRADelta >> 8);

TMR0L = (unsigned char)(usTimerOCRADelta & 0xff);

}

T0CON0bits.T0EN  = 1;

}

void vMBPortTimersDisable()

{

/* Disable any pending timers. */

T0CON0bits.T0EN  = 0;

}

static void prvvTIMERExpiredISR( void )

{

( void )pxMBPortCBTimerExpired(  );

}

void __interrupt(low_priority) INTERRUPT_InterruptManagerLow (void)

{

if(PIE3bits.TMR0IE == 1 && PIR3bits.TMR0IF == 1)

{

//TMR0 = usTimerOCRADelta;

TMR0H = (unsigned char)(usTimerOCRADelta >> 8);

TMR0L = (unsigned char)(usTimerOCRADelta & 0xff);

( void )pxMBPortCBTimerExpired();

PIR3bits.TMR0IF = 0;

}

}

2.4 修改FreeModbus 中demo.c 文件

前面移植时,我们在FreeModbus_app 中添加了一个demo.c 文件,该文件是

从官方源码案列中复制过的。该文件本身就是一个Modbus 测试程序,其内含有Modbus 输入寄存器功能测试。

/* ----------------------- Modbus includes ----------------------------------*/

#include "../include/mb.h"

#include "../include/mbport.h"

//

//* ----------------------- Defines ------------------------------------------*/

#define REG_INPUT_START 0x0001                     //保持寄存器的开始地址

#define REG_INPUT_NREGS 8                      //数据字节数

#define REG_HOLDING_START  0x0001             //保持寄存器的开始地址

#define REG_HOLDING_NREGS  8                  //数据字节数

/* ----------------------- Static variables ---------------------------------*/

static USHORT   usRegInputStart = REG_INPUT_START;

static USHORT   usRegInputBuf[REG_INPUT_NREGS] =

{0x1111,0x2222,0x3333,0x4444,0x5555,0x6666,0x7777,0x8888};

static USHORT         usRegHoldingStart = REG_HOLDING_START;

static USHORT         usRegHoldingBuf[REG_HOLDING_NREGS] =

{0x1111,0x2222,0x3333,0x4444,0x5555,0x6666,0x7777,0x8888};

/* ----------------------- Start implementation -----------------------------*/

/*===================================================================

function name: eMBRegInputCB()

Description: 0x04 输入寄存器处理函数,将数据放入BUF

Input Parameters:

Returncode:

====================================================================*/

eMBErrorCode

eMBRegInputCB( UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNRegs )

{

eMBErrorCode    eStatus = MB_ENOERR;

int             iRegIndex;

if( ( usAddress >= REG_INPUT_START )

&& ( usAddress + usNRegs <= REG_INPUT_START + REG_INPUT_NREGS ) )

{

iRegIndex = ( int )( usAddress - usRegInputStart );

while( usNRegs > 0 )

{

*pucRegBuffer++ =

( unsigned char )( usRegInputBuf[iRegIndex] >> 8 );

*pucRegBuffer++ =

( unsigned char )( usRegInputBuf[iRegIndex] & 0xFF );

iRegIndex++;

usNRegs--;

}

}

else

{

eStatus = MB_ENOREG;

}

return eStatus;

}

/*===================================================================

function name: eMBRegHoldingCB()

Description: 0x03,0x06.0x10 保持寄存器处理函数,将数据放入BUF

Input Parameters:

Returncode:

====================================================================*/

eMBErrorCode

eMBRegHoldingCB( UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNRegs,

eMBRegisterMode eMode )

{

eMBErrorCode    eStatus = MB_ENOERR;

int             iRegIndex;

if( ( usAddress >= REG_HOLDING_START )

&& ( usAddress + usNRegs <= REG_HOLDING_START + REG_HOLDING_NREGS ) )

{

iRegIndex = ( int )( usAddress - usRegInputStart );

while( usNRegs > 0 )

{

*pucRegBuffer++ =

( unsigned char )( usRegHoldingBuf[iRegIndex] >> 8 );

*pucRegBuffer++ =

( unsigned char )( usRegHoldingBuf[iRegIndex] & 0xFF );

iRegIndex++;

usNRegs--;

}

}

else

{

eStatus = MB_ENOREG;

}

return eStatus;

}

3 ModbusPoll 安装和使用

3.1、ModbusPoll 安装

FreeModbus 只能作为从机使用,这就需要其它的工具ModbusPoll 来模拟主机调试。我们实验使用的是Modbus RTU 模式。调试工具 ModbusPoll 我们已经提供,在我们提供的资料文件  --- Modbus源码及调试工具 文件夹里。

将其解压后,选择对应的系统版本双击安装即可,安装过程这里就不在说明。

安装成功后,桌面会有一个快捷键图标,如下:

双击打开 ModbusPoll工具,提示 No connection

现在我们要将从机(也就是我们PIC18F47Q84实验板)与PC 机连接,即使用一个RS232 转RS485 模块和一条USB转RS232 线。RS232 转RS485 模块的RS485 端与开发板的485 的A、B 一一对应连接,不能交叉。RS232 转RS485 模块的RS232 端与USB 转RS232 线连接,该线另一端与电脑USB 口连接,连接示意图如下:

硬件线路连接好后,开始对ModbusPoll 软件设置,

因为软件没有破解,所以只有30天试用时间,这里我们先OK

弹出如下对话框,开始设置串口参数:

点击OK 后,画面显示无效的数据地址,这是因为我们还没有配置对应的寄存器地址和数据量。

3.2、实验现象

首先来测试下读保持寄存器03H 功能,我们点击Setup --- Read/Write Definition

因为程序中配置的O3H 保持寄存器数量为8,所以要对应修改。点击OK 后,

软件现象如下:

这些数据和我们写入寄存器的数据不一样,因为默认该软件是以10进制数显示,而我们写入的是16 进制数,所以在软件中设置下显示方式即可。选中要查看的数据,鼠标右键,选中格式HEX 即可。如下:

结果如下,和我们发送的数据一样,说明FreeModbus 移植正确。

如需测试其它功能,在此基础上,添加自己的代码就可以,操作上大同小异。

PIC18单片机移植FreeModbus相关推荐

  1. 单片机移植freemodbus从机(STM32、GD32、瑞萨、国民技术等)

    从github下载:https://github.com/cwalter-at/freemodbus 无法下载或者下载太慢可以用资源下载,无需积分.[freeModbus从机源码下载] 示例代码 一. ...

  2. 移植FreeModbus

    freeemodbus百度百科(介绍比较详细) https://baike.baidu.com/item/freemodbus/7566841?fr=aladdin STMC2CubeMX | STM ...

  3. STM32CubeMX | Modbus RTU 主机协议栈实现(国产单片机、FreeModbus无缝使用)

    STM32CubeMX | Modbus RTU 主机协议栈实现 目录 1.前言 2.协议栈API介绍 2.1 控制结构 2.2 主机读线圈状态(CMD1) 2.2 主机读离散量输入(CMD2) 2. ...

  4. 手把手教你移植FreeModbus到STM32【看评论区引导,领取全套资料包】

    为什么要移植freemodbus 大家好,近期由于一个小项目的需要,要用到Modbus协议进行通信.相信各位工作的小伙伴们,或多或少都要跟Modbus打交道吧.那么,Modbus协议的重要性我自不必多 ...

  5. 华大单片机移植TencentOS

    华大单片机移植TencentOS-Tiny 文章目录 华大单片机移植TencentOS-Tiny 一.参考链接: 二.资料准备 三.开始移植 四.移植完成-测试 五.总结 一.参考链接: 1.Tiny ...

  6. 华大单片机移植RTThread操作系统

    华大单片机移植RTThread-国产操作系统 文章目录 华大单片机移植RTThread-国产操作系统 1.华大单片机型号选择 2.RTThread源码下载 2.1 .资料说明 2.3.源码下载 3.移 ...

  7. IIC模拟协议华大单片机移植

    IIC模拟协议华大单片机移植 本文所用为 HC32L136K8TA 使用Gpio10== SDA GpioC11==SCL 先把关键性 宏定义说明 //引脚声明 #define SCL_PORT Gp ...

  8. 如何从一款单片机移植到另一款单片机

    如何从一款单片机移植到另一款单片机 目录 如何从一款单片机移植到另一款单片机 前言 1 硬件移植 2 软件移植 2.1 移植原因 2.2 移植原理 2.3 固件库之间的移植方法 2.3 非固件库之间的 ...

  9. 移植Freemodbus到STM32(基于CubeMX,HAL库)-避坑篇

    具体Freemodbus移植到STM32步骤参考: STMC2CubeMX | STM32 HAL库移植FreeModbus详细步骤 基于STM32HAL库移植FreeModbus FreeModbu ...

  10. STM32F103/107 移植Freemodbus RTU

    1.简介 FreeMODBUS一个奥地利人写的Modbus协议.它是一个针对嵌入式应用的一个免费(自由)的通用MODBUS协议的移植.Modbus是一个工业制造环境中应用的一个通用协议.Modbus通 ...

最新文章

  1. JAVA 多用户商城系统b2b2c-Spring Cloud常见问题与总结(一)
  2. 提升应用视觉Android效果的10个UI技巧
  3. 面板php,自己的路面板添加php扩展
  4. 比特币交易的脚本如何执行
  5. centos7查看当前系统时间、_CentOS 7修改系统时间及硬件时间
  6. xss php漏洞扫描工具,XSpear:一款强大的XSS漏洞扫描器工具
  7. 网页点击链接,跳转qq添加好友
  8. html5妇女节游戏,2020三八妇女节趣味游戏大全_妇女节可以举办哪些活动
  9. x58添加uefi_x58 主板 使用 pcie nvme ssd 引导启动
  10. Python爬虫实战之三:requests-百度/360搜索关键词提交
  11. 批量改变图片尺寸大小的方法!一分钟搞定!
  12. STM32L431 I2S/IIS slaver使用
  13. QPalette类详细使用方法
  14. Prototype如何使用?
  15. 过滤器与拦截器的区别?
  16. hive SQL优化
  17. java开源bs系统_WCP是一套BS架构的开源知识管理系统、知识库系统。它.PDF
  18. 自动化测试框架(从robotframework到hyrobot(黑羽robot) python语言)
  19. python selenium高级教程_selenium(python)教程
  20. vuejs视图不能及时更新的问题 ,深入响应式原理

热门文章

  1. Web接入QQ登陆简单入门操作
  2. SCSI设备IO过程:磁盘上线与IO过程
  3. 2021-11-12 应用密码学:协议、算法与C源程序(目录)
  4. Phase2 Day22 JDBC
  5. “绿坝—花季护航”使用全攻略
  6. Win7+vmware+xpsp3+vs2010驱动开发环境搭建及调试方法
  7. Redis集群:./redis-trib.rb:24:in `require': no such file to load -- rubygems
  8. ubuntu安装QQ教程
  9. Ubuntu下安装nvidia显卡驱动
  10. 工具推荐:JDownloader - 下载网盘资料的好工具