h%o%fH&F! FreeMODBUS一个奥地利人写的Modbus协议。它是一个针对嵌入式应用的一个免费(自由)的通用MODBUS协议的移植。Modbus是一个工业制造环境中应用的一个通用协议.。
.GsV>H FreeMODBUS最新版本V1.5。下载地址:
http://www.freemodbus.org/index.php?idx=5 Gy9$wH@8 下面进行的移植基于STM8S单片机的官方固件库
8>trS=;n 1、物理层接口文件修改(具体应修改接口文件portserial.c及porttimer.c)
K{x\4 portserial.c文件中:
X}=n:Ql'YY void vMBPortSerialEnable( BOOL xRxEnable, BOOL xTxEnable)函数设置串口状态。当xRxEnable为真时,应使能串口接收及接收
中断。在RS485通讯系统中,还要注意将RS485接口芯片设为接收使能状态;当xTxEnable为真时,应使能串口发送及发送中断。在RS485通讯系统中,还要注意将RS485接口芯片设为发送使能状态。voidvMBPortSerialEnable( BOOL xRxEnable, BOOL xTxEnable ) //控制串口的收发中断
@GBxL*e {
-|J"s$yO4 if(TRUE==xRxEnable)
m4TE5q% 3 {
^WHE$4U` UART1_ITConfig(UART1_IT_RXNE, ENABLE);
@KWb+?_H{< }
p31NIf` else
*i\Qo {
i'a?kSy UART1_ITConfig(UART1_IT_RXNE, DISABLE);
O-ew%@_ }
OP~HdocB ge,H-8'Z if(TRUE==xTxEnable)
9*2[B"5 {
VeGL) UART1_ITConfig(UART1_IT_TXE,ENABLE);
"8c@sHk(w }
&qMPq-> else
)jU)_To {
nc~F_i= UART1_ITConfig(UART1_IT_TXE, DISABLE);
I
CZ4A{I }
f* !j[U/r_ }
W,4QzcQR BOOL xMBPortSerialInit( UCHAR ucPORT, ULONG ulBaudRate, UCHARucDataBits, eMBParity eParity)函数初始化串行通讯端口。参数ucPORT可以忽略;参数ulBaudRate是通讯端口的波特率,应根据此数值设置所使用硬件端口的波特率;参数ucDataBits为通讯时所使用的数据位宽,注意,若使用RTU模式,则有ucDataBits=8,若使用ASCII模式,则有ucDataBits=7,应根据此参数设置所使用硬件端口的数据位宽;eParity为校验方式,eParity=MB_PAR_NONE为无校验,此时硬件端口应设置为无校验方式及两个停止位,eParity=MB_PAR_ODD为奇校验,此时硬件端口应设置为奇校验方式及一个停止位,eParity= MB_PAR_EVEN为偶校验,此时硬件端口应设置为偶校验方式及一个停止位。函数返回值务必为TRUE。
bHPYp5UwN BOOL xMBPortSerialInit( UCHAR ucPORT, ULONG ulBaudRate, UCHAR ucDataBits, eMBParity eParity )
= 6tHsN23 {
kjW+QT?T& UART1_WordLength_TypeDef databit;
r#+d&.| UART1_Parity_TypeDef parity;
NV)!7~r}: UART1_DeInit();
R%Qf7Q if(8==ucDataBits)
2>|dF~" {
VCu{&Sh* databit = UART1_WORDLENGTH_8D;
?i)f^O }
}E+!91't.^ else
qHsUP;7 {
Ager$uC databit = UART1_WORDLENGTH_9D;
U1@IX4^2` }
pA`+hQNN if(MB_PAR_NONE == eParity)
7Ilm{@b= {
{kp-h2I, parity = UART1_PARITY_NO;
b_,|>U }
!$DIc else if(MB_PAR_ODD == eParity)
{p)",)td {
IYqBQnX}oM parity = UART1_PARITY_ODD;
*"R|4"uy }
4FEk5D else
g+DzscIT {
+~'865 { parity = UART1_PARITY_EVEN;
cmBB[pk\ }
w ihH?~] UART1_Init((uint32_t)ulBaudRate,databit, UART1_STOPBITS_1, parity,
~Cl){8o UART1_SYNCMODE_CLOCK_DISABLE, UART1_MODE_TXRX_ENABLE);
'Hc-~l>D /* ENABLE the USARTx */
2]I4M[|&z UART1_ITConfig(UART1_IT_TXE, ENABLE);
Cnnh7` return TRUE;
5 elw~u
}
bnm
P{Ps BOOL xMBPortSerialPutByte( CHAR ucByte )通讯端口发送一字节数据函数。在此函数中编写发送一字节数据的函数。注意,由于使用的是中断发送,故只需将数据放到发送寄存器即可。函数返回值务必为TRUE。
bIGHGd BOOLxMBPortSerialPutByte( CHAR ucByte )
@WJgWJm {
^w(p8G_-w UART1_SendData8(ucByte);
W [Of|? return TRUE;
[!!o-9b }
;E@G`=0St BOOL xMBPortSerialGetByte( CHAR * pucByte )通讯端口接收一字节数据函数。在此函数中编写接收的函数。由于使用的是中断接收,只需将接收寄存器的值放到* pucByte即可。函数返回值务必为TRUE。
e.]K L(' BOOL xMBPortSerialGetByte( CHAR * pucByte )
0="%Y^N {
r9X?PA0f *pucByte = UART1_ReceiveData8();
nFE4qm return TRUE;
:Mb%A }
L~_9_9c void prvvUARTRxISR( void )和void prvvUARTTxReadyISR( void )无需修改。
7Ok;Lt!x portserial.c文件修改
g1XZ5P} f BOOL xMBPortTimersInit( USHORT usTim1Timerout50us )初始化超时定时器播函数。根据所使用的硬件初始化超时定时器,使之能产生中断时间为usTim1Timerout50us*50us的中断,本例移植采用
单片机的TIM4定时器。函数返回值务必为TRUE。
&@%W29: BOOL xMBPortTimersInit( USHORT usTim1Timerout50us )
=Qh\D {
6^uq?
/* TIM4 configuration:
8*8Zc/{ - TIM4CLK is set to 16 MHz, the TIM4 Prescaler is equal to 128 so the TIM1 counter
< XP9@t&
clock used is 16 MHz / 128 = 125 000 Hz
@b"t]#V(E - With 125 000 Hz we can generate time base:
JXCCTUO max time base is 2.048 ms if TIM4_PERIOD = 255 --> (255 + 1) / 125000 = 2.048 ms
aD=a , min time base is 0.016 ms if TIM4_PERIOD = 1 --> ( 1 + 1) / 125000 = 0.016 ms
S?RN?1 --> 16us
(EK"V'; - In this example we need to generate a time base equal to 50us
*_R]*o!W' so TIM4_PERIOD = (0.00005 * 125000 - 1) = 5 */
n,=VQOu /* Time base configuration */
)_{dWf1 TIM4_TimeBaseInit(TIM4_PRESCALER_128, 5);
RMd[Yr2e /* Clear TIM4 update flag */
@.G[s)x TIM4_ClearFlag(TIM4_FLAG_UPDATE);
x;-.
ZVF /* Enable update interrupt */
s >e=?W TIM4_ITConfig(TIM4_IT_UPDATE, ENABLE);
P/xKnm~ /* enable interrupts */
9UKp?SIF // enableInterrupts();
'6Ay&A3N] /* Enable TIM4 */
>wb'QzF: TIM4_Cmd(ENABLE);
ktj]:rCkF return TRUE;;
D_/^+H]1 }
Qi_>Mg`x void vMBPortTimersEnable( )使能超时定时器函数。需在此函数中清除中断标志位、清零定时器
计数值,并重新使能定时器中断。
:/:.Kb void vMBPortTimersEnable( ) //打开时钟
#k_HN}B {
!6s"]WvF /* Clear TIM4 update flag */
=:D aS`~V TIM4_ClearFlag(TIM4_FLAG_UPDATE);
JAAI_gSR3 /* Enable update interrupt */
Q>/C*@ TIM4_ITConfig(TIM4_IT_UPDATE, ENABLE);
Ynp{u`? TIM4_SetCounter(0x00);
_/Gczy4)# /* Enable TIM4 */
`9)t[7 TIM4_Cmd(ENABLE);
b8LoIY* }
NG void vMBPortTimersDisable( )关闭超时定时器函数。在此函数中清零定时器计数值,并关闭定时器中断。
hGd<<\ void vMBPortTimersDisable( ) //关闭时钟
70f Klp {
Y|8:;u' TIM4_Cmd(DISABLE);
>tO`r.5u9 TIM4_SetCounter(0x00);
5QPM t^ TIM4_ITConfig(TIM4_IT_UPDATE, DISABLE);
<@}I0 TIM4_ClearFlag(TIM4_FLAG_UPDATE);
Sp7ld7c }
|;.o8} void prvvTIMERExpiredISR( void )函数不需修改。
Np'2}6P 2、 应用层回函数的修改
*g y{] 在应用层,用户需要定义所需要使用的寄存器,并修改对应的回函数。
wNQ*t-K eMBErrorCode eMBRegInputCB( UCHAR * pucRegBuffer, USHORT usAddress,USHORT usNRegs)输入寄存器回函数。* pucRegBuffer为协议中所需的数据,usAddress为输入寄存器地址,usNRegs为访问寄存器的个数,eMode为访问类型(MB_REG_READ为读保持寄存器,MB_REG_WRITE为写保持寄存器)。用户应根据要访问的寄存器地址usAddress将相应输入寄存器的值按顺序添加到pucRegBuffer中,或将协议中的数据根据要访问的寄存器地址usAddress放到相应保持寄存器中。
w;k):;$ eMBErrorCode eMBRegInputCB( UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNRegs )
'd+NVj{C {
]xX$<@HR eMBErrorCode eStatus = MB_ENOERR;
?CC"Yij int iRegIndex;
8<.C3m
6h if( ( usAddress >= REG_INPUT_START )&& ( usAddress + usNRegs <= REG_INPUT_START + REG_INPUT_NREGS ) )
WcHgBbNe {
G
16!eDMt iRegIndex = ( int )( usAddress - usRegInputStart );
qw@puw@D while( usNRegs > 0 )
p"l3e9&'j {
i/~1F_ *pucRegBuffer++ =
`}BF${vF ( unsigned char )( usRegInputBuf[iRegIndex] >> 8 );
oI}kH=<, *pucRegBuffer++ =
U
f|>
(C ( unsigned char )( usRegInputBuf[iRegIndex] & 0xFF );
jy giG&H iRegIndex++;
HPb]Zj usNRegs--;
}?z@rt^ }
Ix(?fO#uNF }
Mk=mT3=# else
TjHwjRa {
A5i :x$ww eStatus = MB_ENOREG;
iC W*]U }
%^1cyk return eStatus;
?t+5s] }
ow0!%|fO eMBErrorCode eMBRegHoldingCB( UCHAR * pucRegBuffer, USHORT usAddress,USHORT usNRegs, eMBRegisterMode eMode)保持寄存器回函数。* pucRegBuffer为要协议中的数据,usAddress为输入寄存器地址,usNRegs为访问寄存器的个数,eMode为访问类型(MB_REG_READ为读保持寄存器,MB_REG_WRITE为写保持寄存器)。用户应根据要访问的寄存器地址usAddress将相应输入寄存器的值按顺序添加到pucRegBuffer中,或将协议中的数据根据要访问的寄存器地址usAddress放到相应保持寄存器中。
yaG= j eMBErrorCode eMBRegHoldingCB( UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNRegs, eMBRegisterMode eMode )
VH=S?_RY> {
U$
F{nZ1 eMBErrorCode eStatus = MB_ENOERR;
z I+\Oll#Q int iRegIndex;
AX= 1b,s u16 *PRT=(u16*)pucRegBuffer;
4O;OjUI0a if( ( usAddress >= REG_HOLDING_START ) && ( usAddress + usNRegs <= REG_HOLDING_START + REG_HOLDING_NREGS ) )
mt5KbA>nU {
M/):e$S iRegIndex = ( int )( usAddress - usRegHoldingStart );
ep=qf/vd< switch ( eMode )
1j:Wh {
wE@'ap# case MB_REG_READ:
\0mb
3Q' while( usNRegs > 0 )
lY{FSGp {
G7)Fk%> //// *PRT++ = __REV16(usRegHoldingBuf[iRegIndex++]); //数据序转 REV16.W
/v/C<] H|ER
*pucRegBuffer++ = ( unsigned char )( usRegHoldingBuf[iRegIndex] >> 8 );
jS+AGE?5e *pucRegBuffer++ = ( unsigned char )( usRegHoldingBuf[iRegIndex] & 0xFF );
8}fu,$$5 iRegIndex++;
mcn 2Wt usNRegs--;
W - }
+ +aL4: break;
-jn WZ5. OM|Fwr$ case MB_REG_WRITE:
F29va while( usNRegs > 0 )
'yV?*a {
-0_d/'d //// usRegHoldingBuf[iRegIndex++] = __REV16(*PRT++); //数据序转 REV16.W
59zENUYl usRegHoldingBuf[iRegIndex] = *pucRegBuffer++ << 8;
qwK2WE%T usRegHoldingBuf[iRegIndex] |= *pucRegBuffer++;
6gT5O]]#o iRegIndex++;
<JV"@H= usNRegs--;
rQEyD }
RPIyO }
C=s1R;"H }
aB]m*~ else
$b<6y/" {
k51Eyy50( eStatus = MB_ENOREG;
{@<J_A }
u$D*tqxG return eStatus;
t\RF=BbJJ }
bu=?N eMBErrorCode eMBRegCoilsCB( UCHAR * pucRegBuffer, USHORT usAddress,USHORT usNCoils, eMBRegisterMode eMode )读/写开关寄存器函数。* pucRegBuffer为要添加到协议中的数据,usAddress为地址,usNCoils为要访问的个数,eMode为访问类型(MB_REG_READ为读状态,MB_REG_WRITE为写)。用户应根据要访问的地址usAddress将相应的值按顺序添加到pucRegBuffer中,或将协议中的数据根据要访问的地址usAddress放到相应地址中。
f8SL3+v eMBErrorCode eMBRegCoilsCB( UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNCoils, eMBRegisterMode eMode )
etoo
#h"]1 {
Q 2A7mGN ( void )pucRegBuffer;
Up:<=Kgci ( void )usAddress;
@h*fFiY&{ ( void )usNCoils;
}g3+{\x8 ( void )eMode;
*loOiM\5a return MB_ENOREG;
jS;J:$>^ }
U,+[5sbo eMBErrorCode eMBRegDiscreteCB( UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNDiscrete )读开关寄存器函数。
,^gyH
\ eMBErrorCode eMBRegDiscreteCB( UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNDiscrete )
P7
PB t {
`60gFVu ( void )pucRegBuffer;
y!5$/`AF ( void )usAddress;
R(-<BtM!- ( void )usNDiscrete;
w~#nYM=fP! return MB_ENOREG;
_DrJVC~6@ }
->h6j 官方网站下载出问题,为了方便大家,附上FreeMODBUS源码