PIC18单片机移植FreeModbus
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相关推荐
- 单片机移植freemodbus从机(STM32、GD32、瑞萨、国民技术等)
从github下载:https://github.com/cwalter-at/freemodbus 无法下载或者下载太慢可以用资源下载,无需积分.[freeModbus从机源码下载] 示例代码 一. ...
- 移植FreeModbus
freeemodbus百度百科(介绍比较详细) https://baike.baidu.com/item/freemodbus/7566841?fr=aladdin STMC2CubeMX | STM ...
- STM32CubeMX | Modbus RTU 主机协议栈实现(国产单片机、FreeModbus无缝使用)
STM32CubeMX | Modbus RTU 主机协议栈实现 目录 1.前言 2.协议栈API介绍 2.1 控制结构 2.2 主机读线圈状态(CMD1) 2.2 主机读离散量输入(CMD2) 2. ...
- 手把手教你移植FreeModbus到STM32【看评论区引导,领取全套资料包】
为什么要移植freemodbus 大家好,近期由于一个小项目的需要,要用到Modbus协议进行通信.相信各位工作的小伙伴们,或多或少都要跟Modbus打交道吧.那么,Modbus协议的重要性我自不必多 ...
- 华大单片机移植TencentOS
华大单片机移植TencentOS-Tiny 文章目录 华大单片机移植TencentOS-Tiny 一.参考链接: 二.资料准备 三.开始移植 四.移植完成-测试 五.总结 一.参考链接: 1.Tiny ...
- 华大单片机移植RTThread操作系统
华大单片机移植RTThread-国产操作系统 文章目录 华大单片机移植RTThread-国产操作系统 1.华大单片机型号选择 2.RTThread源码下载 2.1 .资料说明 2.3.源码下载 3.移 ...
- IIC模拟协议华大单片机移植
IIC模拟协议华大单片机移植 本文所用为 HC32L136K8TA 使用Gpio10== SDA GpioC11==SCL 先把关键性 宏定义说明 //引脚声明 #define SCL_PORT Gp ...
- 如何从一款单片机移植到另一款单片机
如何从一款单片机移植到另一款单片机 目录 如何从一款单片机移植到另一款单片机 前言 1 硬件移植 2 软件移植 2.1 移植原因 2.2 移植原理 2.3 固件库之间的移植方法 2.3 非固件库之间的 ...
- 移植Freemodbus到STM32(基于CubeMX,HAL库)-避坑篇
具体Freemodbus移植到STM32步骤参考: STMC2CubeMX | STM32 HAL库移植FreeModbus详细步骤 基于STM32HAL库移植FreeModbus FreeModbu ...
- STM32F103/107 移植Freemodbus RTU
1.简介 FreeMODBUS一个奥地利人写的Modbus协议.它是一个针对嵌入式应用的一个免费(自由)的通用MODBUS协议的移植.Modbus是一个工业制造环境中应用的一个通用协议.Modbus通 ...
最新文章
- JAVA 多用户商城系统b2b2c-Spring Cloud常见问题与总结(一)
- 提升应用视觉Android效果的10个UI技巧
- 面板php,自己的路面板添加php扩展
- 比特币交易的脚本如何执行
- centos7查看当前系统时间、_CentOS 7修改系统时间及硬件时间
- xss php漏洞扫描工具,XSpear:一款强大的XSS漏洞扫描器工具
- 网页点击链接,跳转qq添加好友
- html5妇女节游戏,2020三八妇女节趣味游戏大全_妇女节可以举办哪些活动
- x58添加uefi_x58 主板 使用 pcie nvme ssd 引导启动
- Python爬虫实战之三:requests-百度/360搜索关键词提交
- 批量改变图片尺寸大小的方法!一分钟搞定!
- STM32L431 I2S/IIS slaver使用
- QPalette类详细使用方法
- Prototype如何使用?
- 过滤器与拦截器的区别?
- hive SQL优化
- java开源bs系统_WCP是一套BS架构的开源知识管理系统、知识库系统。它.PDF
- 自动化测试框架(从robotframework到hyrobot(黑羽robot) python语言)
- python selenium高级教程_selenium(python)教程
- vuejs视图不能及时更新的问题 ,深入响应式原理
热门文章
- Web接入QQ登陆简单入门操作
- SCSI设备IO过程:磁盘上线与IO过程
- 2021-11-12 应用密码学:协议、算法与C源程序(目录)
- Phase2 Day22 JDBC
- “绿坝—花季护航”使用全攻略
- Win7+vmware+xpsp3+vs2010驱动开发环境搭建及调试方法
- Redis集群:./redis-trib.rb:24:in `require': no such file to load -- rubygems
- ubuntu安装QQ教程
- Ubuntu下安装nvidia显卡驱动
- 工具推荐:JDownloader - 下载网盘资料的好工具