本帖最后由 八度空间 于 2016-3-8 08:18 编辑
前段时间了解了一下Modbus协议,移植了一次,不成功
http://www.openedv.com/thread-67471-1-1.html,不过找到方法之后,再次完善
在官网上面下载源码,度娘上面有些被人家修改过的,喜欢原汁原味的朋友去官网上下载,本贴附件也附上源码
开发平台:原子哥探索者开发板V2.2
编译环境:MDK5.17
STM32官方库函数:V1.5.1
FreeModbu版本:V1.5.0
一、解压源码后,看到文件目录结构
文件夹demo就是官方针对不同平台移植的测试代码
文件夹doc是一些说明性文档
文件夹modbus就是功能实现的源码所在了
文件夹tools是上位机软件
二、建立工程
2.1、新建一个工程(这个就随意了),建立文件夹FreeModbus,将上面提到的modbus文件夹整个拷贝到工程的FreeModbus文件夹中
2.2、进入demo文件夹,看到有各个平台的测试代码文件夹,没看到STM32的,但是看到BARE这个不带任何平台的代码文件,将里边的port文件夹拷贝到FreeModbus文件夹中
2.3、打开MDK(用IAR也行,随意了),建立工程。。。(省略1万字)
三、添加代码
3.1、打开portserial.c文件,这个是移植串口的,不管是ASCII模式还是RTU模式都需要串口支持的,void vMBPortSerialEnable( BOOL xRxEnable, BOOL xTxEnable )函数,使能或失能串口的,移植代码如下
[mw_shl_code=applescript,true]void
vMBPortSerialEnable( BOOL xRxEnable, BOOL xTxEnable )
{
/* If xRXEnable enable serial receive interrupts. If xTxENable enable
* transmitter empty interrupts.
*/
if (xRxEnable) //接收使能
{
USART_ITConfig(USART2, USART_IT_RXNE, ENABLE); //使能接收中断
GPIO_ResetBits(GPIOG, GPIO_Pin_8); //接收
}
else //失能
{
USART_ITConfig(USART2, USART_IT_RXNE, DISABLE); //失能接收中断
GPIO_SetBits(GPIOG, GPIO_Pin_8); //恢复发送
}
if (xTxEnable) //发送使能
{
USART_ITConfig(USART2, USART_IT_TC, ENABLE); //使能
GPIO_SetBits(GPIOG, GPIO_Pin_8); //发送
}
else //失能
{
USART_ITConfig(USART2, USART_IT_TC, DISABLE); //失能
GPIO_ResetBits(GPIOG, GPIO_Pin_8); //设置接收
}
}[/mw_shl_code]
3.1.1、串口初始化函数BOOL xMBPortSerialInit( UCHAR ucPORT, ULONG ulBaudRate, UCHAR ucDataBits, eMBParity eParity ),使用的是串口2进行通讯
[mw_shl_code=applescript,true]BOOL
xMBPortSerialInit( UCHAR ucPORT, ULONG ulBaudRate, UCHAR ucDataBits, eMBParity eParity )
{
// return FALSE;
GPIO_InitTypeDef GPIO_InitStructure;
USART_InitTypeDef USART_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
(void)ucPORT; //不修改串口号
(void)ucDataBits; //不修改数据位长度
(void)eParity; //不修改检验格式
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA | RCC_AHB1Periph_GPIOG, ENABLE);
RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2, ENABLE);
//
//管脚复用
//
GPIO_PinAFConfig(GPIOA, GPIO_PinSource2, GPIO_AF_USART2);
GPIO_PinAFConfig(GPIOA, GPIO_PinSource3, GPIO_AF_USART2);
//
//发送管脚 PA.02
//接收管脚 PA.03
//
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2 | GPIO_Pin_3;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;
GPIO_Init(GPIOA, &GPIO_InitStructure);
//
//485芯片发送接收控制管脚
//
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;
GPIO_Init(GPIOG, &GPIO_InitStructure);
//
//配置串口参数
//
USART_InitStructure.USART_BaudRate = ulBaudRate; //只修改波特率
USART_InitStructure.USART_WordLength = USART_WordLength_8b;
USART_InitStructure.USART_StopBits = USART_StopBits_1;
USART_InitStructure.USART_Parity = USART_Parity_No;
USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
USART_InitStructure.USART_Parity = USART_Mode_Rx | USART_Mode_Tx;
USART_Init(USART2, &USART_InitStructure);
//
//使能串口
//
USART_Cmd(USART2, ENABLE);
//
//配置中断优先级
//
NVIC_InitStructure.NVIC_IRQChannel = USART2_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
return TRUE;
}[/mw_shl_code]
3.1.2、发送一个字节函数BOOL xMBPortSerialPutByte( CHAR ucByte )
[mw_shl_code=applescript,true]BOOL
xMBPortSerialPutByte( CHAR ucByte )
{
/* Put a byte in the UARTs transmit buffer. This function is called
* by the protocol stack if pxMBFrameCBTransmitterEmpty( ) has been
* called. */
USART_SendData(USART2, ucByte); //发送一个字节
return TRUE;
}[/mw_shl_code]
3.1.3、接收一个字节函数BOOL xMBPortSerialGetByte( CHAR * pucByte )
[mw_shl_code=applescript,true]BOOL
xMBPortSerialGetByte( CHAR * pucByte )
{
/* Return the byte in the UARTs receive buffer. This function is called
* by the protocol stack after pxMBFrameCBByteReceived( ) has been called.
*/
*pucByte = USART_ReceiveData(USART2); //接收一个字节
return TRUE;
}[/mw_shl_code]
3.1.4、串口中断服务函数void USART2_IRQHandler(void)
[mw_shl_code=applescript,true]/**
*****************************************************************************
* @Name : 串口2中断服务函数
*
* @Brief : none
*
* @Input : none
*
* @Output : none
*
* @Return : none
*****************************************************************************
**/
void USART2_IRQHandler(void)
{
if (USART_GetITStatus(USART2, USART_IT_RXNE) == SET) //接收中断
{
prvvUARTRxISR();
USART_ClearITPendingBit(USART2, USART_IT_RXNE);
}
if (USART_GetITStatus(USART2, USART_IT_TC) == SET) //发送中断
{
prvvUARTTxReadyISR();
USART_ClearITPendingBit(USART2, USART_IT_TC);
}
}[/mw_shl_code]
3.2、打开porttimer.c文件,RTU模式需要定时器支持,定时器初始化函数BOOL xMBPortTimersInit( USHORT usTim1Timerout50us )
[mw_shl_code=applescript,true]BOOL
xMBPortTimersInit( USHORT usTim1Timerout50us )
{
// return FALSE;
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
NVIC_InitTypeDef NVIC_InitStructure;
uint16_t PrescalerValue = 0;
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
//
//HCLK为72MHz
//时基频率72 / (1 + Prescaler) = 20KHz
//
PrescalerValue = (uint16_t)((SystemCoreClock / 20000) - 1);
//
//初始化定时器参数
//
TIM_TimeBaseStructure.TIM_Period = (uint16_t)usTim1Timerout50us;
TIM_TimeBaseStructure.TIM_Prescaler = PrescalerValue;
TIM_TimeBaseStructure.TIM_ClockDivision = 0;
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure);
//
//使能预装
//
TIM_ARRPreloadConfig(TIM2, ENABLE);
//
//初始化中断优先级
//
NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
TIM_ClearITPendingBit(TIM2, TIM_IT_Update);
TIM_ITConfig(TIM2, TIM_IT_Update, DISABLE);
TIM_Cmd(TIM2, DISABLE);
return TRUE;
}[/mw_shl_code]
3.2.1、定时器使能函数void vMBPortTimersEnable( )
[mw_shl_code=applescript,true]void
vMBPortTimersEnable( )
{
/* Enable the timer with the timeout passed to xMBPortTimersInit( ) */
TIM_ClearITPendingBit(TIM2, TIM_IT_Update);
TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE);
TIM_SetCounter(TIM2, 0x00000000);
TIM_Cmd(TIM2, ENABLE);
}[/mw_shl_code]
3.2.2、定时器失能函数void vMBPortTimersDisable( )
[mw_shl_code=applescript,true]void
vMBPortTimersDisable( )
{
/* Disable any pending timers. */
TIM_ClearITPendingBit(TIM2, TIM_IT_Update);
TIM_ITConfig(TIM2, TIM_IT_Update, DISABLE);
TIM_SetCounter(TIM2, 0x00000000);
TIM_Cmd(TIM2, DISABLE);
}[/mw_shl_code]
3.2.3、定时器中断服务函数void TIM2_IRQHandler(void)
[mw_shl_code=applescript,true]/**
*****************************************************************************
* @Name : 定时器4中断服务函数
*
* @Brief : none
*
* @Input : none
*
* @Output : none
*
* @Return : none
*****************************************************************************
**/
void TIM2_IRQHandler(void)
{
if (TIM_GetITStatus(TIM2, TIM_IT_Update) != RESET)
{
TIM_ClearITPendingBit(TIM2, TIM_IT_Update);
prvvTIMERExpiredISR();
}
}[/mw_shl_code]
四、打开port.c文件,这个文件我是从别的工程拷贝过来的
4.1、补充完成以下两个函数,这两个函数跟中断有关,开或者关
[mw_shl_code=applescript,true]void
EnterCriticalSection( )
{
__ASM volatile("cpsid i");
}
void
ExitCriticalSection( )
{
__ASM volatile("cpsie i");
}[/mw_shl_code]
4.2、定义相关寄存器地址什么的,详细看工程文件
4.3、分别完善一下函数
4.3.1、操作输入寄存器eMBErrorCode eMBRegInputCB( UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNRegs )
4.3.2、操作保持寄存器eMBErrorCode eMBRegHoldingCB( UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNRegs, eMBRegisterMode eMode )
4.3.3、操作线圈eMBErrorCode eMBRegCoilsCB( UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNCoils, eMBRegisterMode eMode )
4.3.4、操作离散寄存器eMBErrorCode eMBRegDiscreteCB( UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNDiscrete )
4.4、打开port.h文件,完善两个宏定义
4.4.1、#define ENTER_CRITICAL_SECTION( )也可以空着
4.4.2、#define EXIT_CRITICAL_SECTION( )
五、编写main函数
[mw_shl_code=applescript,true]/**
*****************************************************************************
* @Name : 主函数
*
* @Brief : none
*
* @Input : none
*
* @Output : none
*
* @Return : none
*****************************************************************************
**/
int main( void )
{
u8 i = 0;
//
//初始化硬件
//
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
delay_init();
uart_init(38400);
LED_Init();
usmart_dev.init(72);
LCDTFT_Init();
eMBInit(MB_RTU, 0x0A, 1, 38400, MB_PAR_NONE); //初始化FreeModbus
eMBEnable(); //启动FreeModbus
LCD_ShowString(30, 50, 480, 24, 24, "FreeModbus Test.");
while (1)
{
(void)eMBPoll(); //查询数据帧
usRegInputBuf[0]++; //测试读取输入寄存器用
if (usRegInputBuf[0] > 250) //防止超标
{
usRegInputBuf[0] = 0;
}
delay_ms(10);
i++;
if (i >= 50)
{
i = 0;
LED0 = !LED0;
}
}
}[/mw_shl_code]
为了测试我将输入寄存器在while循环中加1
六、编译,问题来了,提示错误
FreeModbus作者使用了assert这货,各种百度,说什么乱七八糟的,没解决什么问题,最后还是自己解决了
http://www.openedv.com/thread-67471-1-1.html
在main函数最后增加代码
[mw_shl_code=applescript,true]#ifdef USE_FULL_ASSERT
/**
* @brief Reports the name of the source file and the source line number
* where the assert_param error has occurred.
* @param file: pointer to the source file name
* @param line: assert_param error line source number
* @retval None
*/
void assert_failed(uint8_t* file, uint32_t line)
{
/* User can add his own implementation to report the file name and line number,
ex: printf("Wrong parameters value: file %s on line %d
", file, line) */
/* Infinite loop */
while (1)
{
}
}
#else
void __aeabi_assert(const char * x1, const char * x2, int x3)
{
}
#endif[/mw_shl_code]
再次编译,发现没错误了,下载到板子上面运行,用串口助手(Modbus调试精灵也行)发送命令0A 04 00 01 00 01 61 71读取输入寄存器,发现没返回数据,各种仿真之后,发现串口中断的问题,前面初始化使用了TC,发送完成中断,打开手册看到了TXE这个,修改编译下载,再次发送命令,这回有返回了,不过数据不对,返回0
在eMBErrorCode eMBRegInputCB( UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNRegs )函数中将usAddress变量显示到液晶上面一看,不对,比发送的地址多了1,跳转到函数eMBFuncReadInputRegister( UCHAR * pucFrame, USHORT * usLen )一看,解释之后来了一句usRegAddress++;,赶紧注释掉,再来,这回对了
接着测试其他的,也发现了最后操作的地址比发送过去的地址多了1,又打开相关的函数文件,都是在地址解释之后有usRegAddress++;这句存在,赶紧注释掉,再来,这回正确了
七、回到刚才的串口发送中断标志TC和TXE的问题上来,对照手册一看
大家对比一下说明,我就不解释了
打开mbrtu.c文件,在下面位置增加一段代码,修正TC中断标志发送的错误
如果大家不想修改源码的话就用TXE这个标志,不过增加了以上代码再使用TXE标志也是没有问题的,无所谓了
好了到此为止,移植算是完成了,继续深究的朋友可以接着,否则就直接用吧
附件上传我调试用到的工具软件
呵呵
一周热门 更多>