本帖最后由 固桐 于 2013-5-27 22:27 编辑
这几天,研究了freemodbus通信协议,然后在stm32上移植。关于freemodbus通信原理,以及通信过程(状态机转换),这里就不说了。 freemodbus使用的库是最新版本freemodbus-v1.5.0,把相应的库文件加载在STM32工程中。移植时,需要用户改写的文件有两个,一个是porttimer.c文件,一个是portserial.c文件。porttimer.c文件主要用于超时判断,本例使用的是定时器2,portserial.c主要使用串口通信的实现,本例使用的是RS485.porttimer.c修改如下
/* ----------------------- Platform includes --------------------------------*/
#include "port.h"
/* ----------------------- Modbus includes ----------------------------------*/
#include "mb.h"
#include "mbport.h"
#include "..\..\include\nvic.h"
#include "..\..\include\stm32f10x_reg.h"
/* ----------------------- Start implementation -----------------------------*/
BOOL
xMBPortTimersInit( USHORT usTim1Timerout50us ) //配置50us时钟
{
uint16_t PrescalerValue = 0;
/* TIM2 clock enable */
RCC->CFGR.B.PPRE1 = 4; //APB1预分频系数
//100:HCLK 2分频
//APB1 不超过36MHz
RCC->APB1ENR.B.TIM2EN = 1;
/* Compute the prescaler value */
PrescalerValue = (uint16_t) (36000000 / 20000) - 1; // 1/20000=50us
/* Time base configuration */
TIM2->ARR=(uint16_t) usTim1Timerout50us;
TIM2->PSC= PrescalerValue;
TIM2->CR1.B.URS = 1;
MY_NVIC_Init(3,3,28,2);
/* TIM IT DISABLE */
TIM2->SR.B.UIF = 0;
TIM2->DIER.B.UIE = 0;
TIM2->CR1.B.CEN = 0;
/* TIM2 DISABLE counter */
return TRUE;;
}
void vMBPortTimersEnable( ) //打开时钟
{
TIM2->SR.B.UIF = 0;
TIM2->DIER.B.UIE = 1;
TIM2->CNT= 0;
TIM2->CR1.B.CEN = 1;
}
void vMBPortTimersDisable( ) //关闭时钟
{
TIM2->CR1.B.CEN = 0;
TIM2->CNT= 0;
TIM2->DIER.B.TIE = 0;
TIM2->DIER.B.UIE = 0;
}
/* Create an ISR which is called whenever the timer has expired. This function
* must then call pxMBPortCBTimerExpired( ) to notify the protocol stack that
* the timer has expired.
*/
void prvvTIMERExpiredISR( void ) //在时钟中断内调用
{
( void )pxMBPortCBTimerExpired( );
}
void TIM2_IRQHandler(void)
{
/* Clear TIM4 Capture Compare1 interrupt pending bit*/
TIM2->SR.B.UIF = 0;
TIM2->SR.B.TIF = 0;
prvvTIMERExpiredISR( );
}
portserial.c文件修改如下
#include "port.h"
/* ----------------------- Modbus includes ----------------------------------*/
#include "mb.h"
#include "mbport.h"
#include "..\..\include\nvic.h"
#include "..\..\include\stm32f10x_reg.h"
#include "mb.h"
#include "mbport.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <math.h>
#include <stdarg.h>
#define RS485_T GPIOA->BSRR.B.SETIO4=1
#define RS485_R GPIOA->BSRR.B.CLRIO4=1
/* ----------------------- Start implementation -----------------------------*/
void
vMBPortSerialEnable( BOOL xRxEnable, BOOL xTxEnable ) //控制串口的收发中断
{
if(TRUE==xRxEnable)
{
USART2->CR1.B.RXNEIE = 1;
}
else
{
USART2->CR1.B.RXNEIE = 0;
}
if(TRUE==xTxEnable)
{
USART2->CR1.B.TXEIE = 1;
}
else
{
USART2->CR1.B.TXEIE = 0;
}
}
* 配置串口 目前除了波特率其他参数无效
BOOL
xMBPortSerialInit( UCHAR ucPORT, ULONG ulBaudRate, UCHAR ucDataBits, eMBParity eParity )
{
float temp;
float temp2;
AFIO->MAPR.B.USART2_REMAP=0; //无重映像
RCC->APB1ENR.B.USART2EN = 1; //USART2时钟开启
GPIOA->CRL.B.MODE2 = 3; // Configure USART2 Tx (PA.2) as alternate function push-pull
GPIOA->CRL.B.CNF2 = 2;
GPIOA->CRL.B.MODE3 = 0; // Configure USART2 Rx (PA.3) as input floating
GPIOA->CRL.B.CNF3 = 1;
temp= (72000000.0/2)/(16*ulBaudRate);
temp2=floor(temp);
temp=temp-temp2;
temp*=16;
USART2->BRR.B.DIV_Fraction=temp;//DIV_Fraction[3:0]:USARTDIV的小数部分
//这4位定义了USART分频器除法因子(USARTDIV)的小数部分。
// 6 9600
USART2->BRR.B.DIV_Mantissa=temp2;
//DIV_Mantissa[11:0]:USARTDIV的整数部分
//这12位定义了USART分频器除法因子(USARTDIV)的整数部分。
//234 9600
USART2->CR1.B.PCE = 0; //0:校验控制被禁止;
USART2->CR1.B.M = 0; //0:一个起始位,8个数据位,n个停止位;
USART2->CR1.B.RE = 1; //1:接受被使能。
USART2->CR1.B.TE = 1; //1:发送被使能。
USART2->CR1.B.RXNEIE = 1; //1:当USART_SR中的ORE或者RXNE为1时,产生USART中断。
USART2->CR1.B.PEIE = 1; //1:当USART_SR中的PE为1时,产生USART中断。
USART2->CR1.B.UE = 1; //1:USART模块使能。
USART2->CR2.B.LBCL = 0; //0:最后一位数据的时钟脉冲不从SCLK输出;
USART2->CR2.B.CPOL = 0; //0:总线空闲时SCLK引脚上保持低电平;
USART2->CR2.B.CLKEN = 0; //0:SCLK引脚被禁止。
USART2->CR2.B.STOP = 0; //00:1个停止位;
USART2->CR2.B.CPHA = 1; //1:时钟二个第边沿进行数据捕获。
USART2->CR3.B.RTSE = 0; //0:RTS硬件流控制被禁止;
USART2->CR3.B.CTSE = 0; //0:CTS硬件流控制被禁止;
MY_NVIC_Init(3,3,38,2);//组2,最低优先级
GPIOA->CRL.B.MODE4 = 3;
GPIOA->CRL.B.CNF4 = 0;
RS485_R;
return TRUE;
}
// 串口发
BOOL
xMBPortSerialPutByte( CHAR ucByte )
{
int i;
RS485_T;
for(i=0;i<1000;i++);
USART2->DR.W= ucByte;
while (!(USART2->SR.B.TC == 1)) ;
RS485_R;
return TRUE;
}
// 串口收
BOOL
xMBPortSerialGetByte( CHAR * pucByte )
{
*pucByte = USART2->DR.W;
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.
*/
void prvvUARTTxReadyISR( void )
{
pxMBFrameCBTransmitterEmpty( );
}
/* 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.
*/
void prvvUARTRxISR( void )
{
pxMBFrameCBByteReceived( );
}
void USART2_IRQHandler(void)
{
if(USART2->CR1.B.RXNEIE==1)
{
prvvUARTRxISR();//接收完成中断
USART2->SR.B.RXNE=0;
}
if(USART2->CR1.B.TXEIE==1)
{
prvvUARTTxReadyISR();//发送完成中断
USART2->SR.B.TC=0;
}
}
另外在主函数中,加入测试函数如下
#include "mb.h"
#include "..\..\include\adc.h"
#include "..\..\include\lm75.h"
#include "..\..\include\timer.h"
#include "..\..\include\simulate.h"
#include "..\..\include\encode.h"
#include "..\..\include\hardware.h"
#include "..\..\include\systick.h"
unsigned short int usRegInputBuf[10]={0x0000,0xfe02,0x1203,0x1304,0x1405,0x1506,0x1607,0x1708,0x1809};
unsigned short int *usRegHoldingBuf=usRegInputBuf; //一个测试用的 寄存器数组 地址0-7
unsigned char REG_INPUT_START=0,REG_HOLDING_START=0;
unsigned char REG_INPUT_NREGS=8,REG_HOLDING_NREGS=8;
unsigned char usRegInputStart=0,usRegHoldingStart=0;
//读数字寄存器 功能码0x04
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;
}
// 寄存器的读写函数 支持的命令为读 0x03 和写0x06
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 - usRegHoldingStart );
switch ( eMode )
{
case MB_REG_READ:
if(usAddress==1)
{
*pucRegBuffer++ = ( unsigned char )( adc.read(1) >> 8 );
*pucRegBuffer++ = ( unsigned char )( adc.read(1) & 0xFF );
}
if(usAddress==2)
{
*pucRegBuffer++ = ( unsigned char )( adc.read(0) >> 8 );
*pucRegBuffer++ = ( unsigned char )( adc.read(0) & 0xFF );
}
if(usAddress==3)
{
*pucRegBuffer++ = ( unsigned char )( adc.read(7) >> 8 );
*pucRegBuffer++ = ( unsigned char )( adc.read(7) & 0xFF );
}
if(usAddress==4)
{
*pucRegBuffer++ = ( unsigned char )( lm75.read() >> 8 );
*pucRegBuffer++ = ( unsigned char )( lm75.read() & 0xFF );
}
if(usAddress==5)
{
timer.refresh();
*pucRegBuffer++ = ( unsigned char )( tim_parameter.Position_relative >> 8 );
*pucRegBuffer++ = ( unsigned char )( tim_parameter.Position_relative &0xFF );
}
if(usAddress==6)
{
timer.refresh();
*pucRegBuffer++ = ( unsigned char )( TIM1->CCR2>> 8 );
*pucRegBuffer++ = ( unsigned char )( TIM1->CCR2&0xFF );
}
break;
case MB_REG_WRITE:
while( usNRegs > 0 )
{
usRegHoldingBuf[iRegIndex] = *pucRegBuffer++ << 8;
usRegHoldingBuf[iRegIndex] |= *pucRegBuffer++;
iRegIndex++;
usNRegs--;
}
}
}
else
{
eStatus = MB_ENOREG;
}
return eStatus;
}
//读/写开关寄存器 0x01 x05
eMBErrorCode
eMBRegCoilsCB( UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNCoils, eMBRegisterMode eMode )
{
( void )pucRegBuffer;
( void )usAddress;
( void )usNCoils;
( void )eMode;
return MB_ENOREG;
}
//读开关寄存器 0x02
eMBErrorCode
eMBRegDiscreteCB( UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNDiscrete )
{
( void )pucRegBuffer;
( void )usAddress;
( void )usNDiscrete;
return MB_ENOREG;
}
经过,测试,本次移植可以读取节点的温度,AD转换值,等参数,实现了预期的效果。
|