最近使用一个2.4G无线模块,做主机从机之间通信,取代原先485有线通信,由于使用的是串口透传模块,简单改了一下标志位,就开始调试程序了。
1、串口助手连无线模块,能发送接收 (正常)
2、主电脑分别当主从机,测试电路板modbus是否正常(不带模块测试) (正常)
3、无线模块分别接到主从机上测试 (不正常)
发现不正常有些慌了,单独测试都没有问题,无线模块就是串口透传,能有啥问题。果断开始debug。
调试开始---------------------------------------------------------------> 一天过去了,没有找到问题。
第二天调试开始--------------------->发现问题了。
串口接收采用的是DMA+空闲中断,在中断中设置断点。
void USART2_IRQHandler(void)
{
u8 res=0;
u8 cout=0;
u8 i = 0;
if(USART_GetITStatus(USART2, USART_IT_IDLE) == SET){ //接收中断
USART_ClearITPendingBit(USART2, USART_IT_IDLE);
res = USART2->SR;
res = USART2->DR; //清usart_IT_IDLE 标志
DMA_Cmd(DMA1_Channel6,DISABLE); // 关闭DMA
cout = MB_UART_RX_LEN - DMA_GetCurrDataCounter(DMA1_Channel6); // 得到接受数据个数
MB_Uart_Rx[cout] = '\0';
if(MB_Uart_Rx[1] == 0x03) // 保持寄存器
{
for(i = 0;iCNDTR=MB_UART_RX_LEN; //重新设置接收个数
DMA_Cmd(DMA1_Channel6,ENABLE); //开启DMA
if(USART_GetITStatus(USART2, USART_IT_PE | USART_IT_FE | USART_IT_NE) != RESET)//出错
{
USART_ClearITPendingBit(USART2, USART_IT_PE | USART_IT_FE | USART_IT_NE);
}
mb_receive_flag = 1;
// receive_flag = 1; //接收数据1
// recflag = 1;
//
}
}
发现全速运行时进入断点,每次只能接收到一个或者2个字节数据,从机反回来的是7个字节的数据,说明接收出现了丢包现象。
分析:
空闲中断+DMA 接收程序在好几个产品中应用,而且有线485也能正常使用,所以怀疑是模块的问题,之前用过其他家的无线模块,遇到过"沾包的现象"即两个数据包发送的时间间隔较短,无线模块认为是一帧数据,从断点接收1-2个字节的数据判断,不是沾包这种情况。进入中断的条件是发生一个字节时间长度空闲,则进入中断。我这里使用的波特率是9600,大概计算一下:
1s 9600个bit位 一个数据大概在10个bit左右 传输一个字节数据大概在1ms 那么空闲中断的超时时间就是1ms左右。果断查找无线模块的数据手册,--------------------------------------------------------------->时间过去好久,翻遍手册没有找到。多亏前些天加了模块原厂的销售,经过沟通-------------------------------------------------------------->无线模块字节与字节直接的时间间隔在1-5ms之间不确定,不能改下。坑爹啊,小厂的无线模块靠不住啊,技术支持不能解决问题,偷懒DMA+空闲中断用不了,要了老命。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。
但是问题还是要解决的!!!!!!!
方案1、采用最原始的一个字节一中断,但是32个从机,一直发数据,那么就是一直进中断,效率极其低下
方案2、继续研究新的接收方法,继续专研技术(偷懒技术)
经过一段时间研发论证分析翻了一堆帖子。总结了新的方法。
方法如下:
串口DMA接收+超时查询 (不用中断)
在原先的程序上小改动一下即可。程序如下
void uart2_init(u32 bound)
{
//GPIO端口设置
GPIO_InitTypeDef GPIO_InitStructure;
USART_InitTypeDef USART_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2, ENABLE); //使能USART2,GPIOA时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); //使能USART2,GPIOA时钟
USART_DeInit(USART2); //复位串口2
//USART2_TX PA.2
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2; //PA.2
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //复用推挽输出
GPIO_Init(GPIOA, &GPIO_InitStructure); //初始化PA2
//USART2_RX PA.3
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;//浮空输入
GPIO_Init(GPIOA, &GPIO_InitStructure); //初始化PA3
//USART 初始化设置
USART_InitStructure.USART_BaudRate = bound;//一般设置为9600;
USART_InitStructure.USART_WordLength = USART_WordLength_8b;//字长为8位数据格式
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(USART2, &USART_InitStructure); //初始化串口
//Usart1 NVIC 配置
NVIC_InitStructure.NVIC_IRQChannel = USART2_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=2 ;//抢占优先级1
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2; //子优先级0
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道使能
NVIC_Init(&NVIC_InitStructure); //根据指定的参数初始化VIC寄存器
// USART_ITConfig(USART2, USART_IT_IDLE, ENABLE);//总线空闲 产生中断
USART_DMACmd(USART2, USART_DMAReq_Rx, ENABLE);
// USART_DMACmd(USART2, USART_DMAReq_Tx, ENABLE);
USART_Cmd(USART2, ENABLE); //使能串口
/*uart2DMA配置*/
DMA_DeInit(DMA1_Channel6);
DMA_Initstructure.DMA_PeripheralBaseAddr = (u32)(&USART2->DR);;
DMA_Initstructure.DMA_MemoryBaseAddr = (u32)MB_Uart_Rx;
DMA_Initstructure.DMA_DIR = DMA_DIR_PeripheralSRC;
DMA_Initstructure.DMA_BufferSize = MB_UART_RX_LEN;
DMA_Initstructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
DMA_Initstructure.DMA_MemoryInc =DMA_MemoryInc_Enable;
DMA_Initstructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;
DMA_Initstructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;
DMA_Initstructure.DMA_Mode = DMA_Mode_Normal;
DMA_Initstructure.DMA_Priority = DMA_Priority_High;
DMA_Initstructure.DMA_M2M = DMA_M2M_Disable;
DMA_Init(DMA1_Channel6,&DMA_Initstructure);
DMA_ITConfig(DMA1_Channel6, DMA_IT_TC, ENABLE);
DMA_ITConfig(DMA1_Channel6, DMA_IT_TE, ENABLE);
/* Enable USART2 DMA RX request */
USART_DMACmd(USART2, USART_DMAReq_Rx, ENABLE);
//启动DMA
DMA_Cmd(DMA1_Channel6,ENABLE);
}
void wl24GreceiveData(void)
{
u8 cout=0;
u8 i=0;
DMA_Cmd(DMA1_Channel6,DISABLE); // 关闭DMA
cout = MB_UART_RX_LEN - DMA_GetCurrDataCounter(DMA1_Channel6); // 得到接受数据个数
MB_Uart_Rx[cout] = '\0';
if(MB_Uart_Rx[1] == 0x03) // 保持寄存器
{
for(i = 0;iCNDTR=MB_UART_RX_LEN; //重新设置接收个数
DMA_Cmd(DMA1_Channel6,ENABLE); //开启DMA
}
超时时间设置成100MS 能成才接收数据 。超时读取数据,能正常玩耍了。
最后说一句,使用小厂家的模块虽然便宜,但是即使支持不好,有各种各样的bug,出问题自己解决,如果没有实力精力时间,不要碰便宜的模块,便宜没好货,好货不便宜。
研究无线modbus去了。