前一段时间一直在研究modbus协议的源代码,并且自己用单片机C实现了一下但是其实没什么卵用的,因为根本就不可能用在实际工程中也是自己当时太不懂行情了上网搜了一下我觉得实际人家产品上用的很少应该是自己去写,因为那根本就不合算而且还会出错,又走了弯路我觉得一般的做从站用freemodbus就很好也是因为水平太菜就这个freemodbus也差不多弄了有1个礼拜今天终于搞定了能转起来并能正确的返回数据因此在这里总结一下:
1、使用的单片机是stm32 F103来做移植,裸机移植没有加操作系统(后边在uCOS上移植好了在补充)
2、协议栈是我从官网上下的最新的1.6
一开始我是想移植网上流传的一个周立功公司的协议栈但是我弄了两天觉得太难了因为首先那只是一个文档说的其实也不是很清楚而且要自己移植的部分差别太大。我是把串口部分和定时器部分都移植好了之后发现在主调函数中的eMBPoll()中是靠一个事件来询问的,但是那个事件怎么初始化、GET、POST我都没弄明白所以 我就放弃了主要是他那个说是不仅可以做从栈还支持主站模式因为他那里很巧妙的还每次检查1.5T的时间,还有一个就是我当时不清楚怎么在没有操作系统的状态下进入临界区的没弄明白就放弃了。
就下了一个freemodbus来看了一下网上资料也很多就好多了。源代码我就不说了但是我建议一定要先看懂最重要的是两个文件mb.c和mbrtu.c这两个文件一定要看懂。还有就是在mbport.h中已经给你声明好了你需要自己动手编写的文件,照着那个思路来就可以了。编译的时候要注意最好是把那个MicroLib的勾去掉不然会报错,如果还是想用printf()来调试的话也可以用一个隐式的声明网上很多我就不写了我随便找了一个就可以用。在有就是ENTER_CRITICAL_SECTION()和EXIT_CRITICAL_SECTION()我在网上搜了一下我觉得有两种方法一种是用ARM汇编一种是用标准库里也有两个对应的关总中断和开总中断的函数我看了一下没记住我用的ARM汇编的那种如下
void
EnterCriticalSection( )
{
__ASM volatile("cpsid i");
}
void
ExitCriticalSection( )
{
__ASM volatile("cpsie i");
}
把关键的代码贴出来我觉得把这几项弄清楚应该就没什么问题了:
void main(void)
{
NVIC_Configuration();
eMBInit(MB_RTU, 1, 0, 9600, MB_PAR_NONE);
eMBEnable();
while(1)
{
(void)eMBPoll();
}
}
主函数这套了就比较套路了在官网上就是这么给的也没什么说的。下面我就按照协议的调用流程来贴出来了
串口初始化函数:
void uart_init(u32 bound)
{
GPIO_InitTypeDef GPIO_InitStructure;
USART_InitTypeDef USART_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1|RCC_APB2Periph_GPIOA|RCC_APB2Periph_AFIO, ENABLE);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9; //PA.9
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_Init(GPIOA, &GPIO_InitStructure);
//USART1_RX PA.10
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_Init(GPIOA, &GPIO_InitStructure);
USART_InitStructure.USART_BaudRate = bound;
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_Mode = USART_Mode_Rx | USART_Mode_Tx;
USART_Init(USART1, &USART_InitStructure);
USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);
USART_Cmd(USART1, ENABLE);
}
这个也比较常规了
事件初始化函数
static eMBEventType eQueuedEvent;
static BOOL xEventInQueue;
BOOL
xMBPortEventInit( void )
{
xEventInQueue = FALSE;
return TRUE;
}
时间初始化函数
BOOL
xMBPortTimersInit( USHORT usTim1Timerout50us )
{
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
TIM_TimeBaseStructure.TIM_Period = 50;
TIM_TimeBaseStructure.TIM_Prescaler = (7200 - 1);
TIM_TimeBaseStructure.TIM_ClockDivision = 0x0;
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure);
TIM_Cmd(TIM2, ENABLE);
return TRUE;
}
还有两个时间的函数也比较简单
void
vMBPortTimersEnable( )
{
TIM_ClearITPendingBit(TIM2, TIM_IT_Update);
TIM_SetCounter(TIM2, 0);
//TIM_Cmd(TIM2, ENABLE);
TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE);
}
void
vMBPortTimersDisable( )
{
TIM_SetCounter(TIM2, 0);
//TIM_Cmd(TIM2, DISABLE);
TIM_ITConfig(TIM2, TIM_IT_Update, DISABLE);
}
把串口中断和时钟中断的函数也贴出来
void USART1_IRQHandler(void)
{
//static unsigned int u;
//u++;
if(USART_GetITStatus(USART1, USART_IT_RXNE) == SET)
{
prvvUARTRxISR();
USART_ClearITPendingBit(USART1, USART_IT_RXNE);
//printf("u=%u\r\n",u);
}
if(USART_GetITStatus(USART1, USART_IT_TXE) == SET)
{
//printf("com in ittxe intrupt\r\n");
prvvUARTTxReadyISR();
//USART_ClearITPendingBit(USART1, USART_IT_TC);
}
}
void TIM2_IRQHandler(void)
{
//static unsigned int t;
//t++;
//printf("t=%u",t);
TIMERExpiredISR();
TIM_ClearITPendingBit(TIM2, TIM_IT_Update);
}
还有就是事件的请求和发送函数
BOOL
xMBPortEventPost( eMBEventType eEvent )
{
xEventInQueue = TRUE;
eQueuedEvent = eEvent;
return TRUE;
}
BOOL
xMBPortEventGet( eMBEventType * eEvent )
{
BOOL xEventHappened = FALSE;
if( xEventInQueue )
{
*eEvent = eQueuedEvent;
xEventInQueue = FALSE;
xEventHappened = TRUE;
}
return xEventHappened;
}
这样应该就没什么大问题了,仔细点,在多分析协议源码多花点时间就肯定没有什么问题解决不了。****