OpenEdv-开源电子网

 找回密码
 立即注册
正点原子全套STM32/Linux/FPGA开发资料,上千讲STM32视频教程免费下载...
查看: 8047|回复: 11

串口通信问题,仿PLC,单片机和三菱编程软件,求助

[复制链接]

15

主题

68

帖子

0

精华

中级会员

Rank: 3Rank: 3

积分
289
金钱
289
注册时间
2014-1-2
在线时间
62 小时
发表于 2016-7-13 10:04:13 | 显示全部楼层 |阅读模式
50金钱
本帖最后由 llqzx 于 2016-7-13 10:13 编辑

微信截图_20160713094313.png
这个是我联机FX2N和三菱编程软件得到的通信协议,当测试通信时,软件先发送 05 到下位机,下位机回复 06,然后上位机发送 02 30 30 45 30 32 30 32 03 36 43到下位机,下位机 回复 02 46 36 35 45 03 46 39……再依次进行,但是看监视到的数据和我实际的测试,有以下问题:

1,
因为通信过程中可能单片机串口接收到的数据长度不一致,所以我采用了论坛里的空闲中断方式来接收,然后一帧数据接收完后触发中断,去判断帧头或者功能字是什么进行处理,比如判断 BUF[0]=0x05,就发送0x06,如果是BUF[0]=0x02,BUF[1]=0x30,……再发送别的,思路是这样,但是实际测试, 收到 05 发送 06 没问题,但是再收到一帧数据,就不进中断了,也不发送了,不知道什么原因。而且不知道哪里来的一个7f,看监视,像是单片机发出的,但是明明没有这个。用串口调试软件测试单片机没问题,连续发送都可以进中断,调试了好几天了没头绪。下面贴出代码和截图 微信截图_20160713095018.png
void uart_init(u32 bound)
{
    //GPIO端口设置
    GPIO_InitTypeDef GPIO_InitStructure;
    USART_InitTypeDef USART_InitStructure;
    NVIC_InitTypeDef NVIC_InitStructure;
    DMA_InitTypeDef DMA_InitStructure;
     
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1|RCC_APB2Periph_GPIOA, ENABLE);    //使能USART1,GPIOA时钟
    RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);    //使能DMA传输
     //USART1_TX   PA.9
    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);  

   //Usart1 NVIC 配置

    NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=3 ;//抢占优先级3
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3;        //子优先级3
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;            //IRQ通道使能
    NVIC_Init(&NVIC_InitStructure);    //根据指定的参数初始化VIC寄存器
  
   //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_Even;//无奇偶校验位
    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_IDLE, ENABLE);//开启空闲中断
//    USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);//开启中断


    //相应的DMA配置
    DMA_DeInit(DMA1_Channel5);   //将DMA的通道5寄存器重设为缺省值  串口1对应的是DMA通道5
    DMA_InitStructure.DMA_PeripheralBaseAddr = (u32)&USART1->DR;  //DMA外设ADC基地址
    DMA_InitStructure.DMA_MemoryBaseAddr = (u32)DMA_Rece_Buf;  //DMA内存基地址
    DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;  //数据传输方向,从外设读取发送到内存
    DMA_InitStructure.DMA_BufferSize = DMA_Rec_Len;  //DMA通道的DMA缓存的大小
    DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;  //外设地址寄存器不变
    DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;  //内存地址寄存器递增
    DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;  //数据宽度为8位
    DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte; //数据宽度为8位
    DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;  //工作在正常缓存模式
    DMA_InitStructure.DMA_Priority = DMA_Priority_Medium; //DMA通道 x拥有中优先级
    DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;  //DMA通道x没有设置为内存到内存传输
    DMA_Init(DMA1_Channel5, &DMA_InitStructure);  //根据DMA_InitStruct中指定的参数初始化DMA的通道USART1_Tx_DMA_Channel所标识的寄存器
   
    USART_DMACmd(USART1,USART_DMAReq_Rx,ENABLE);   //使能串口1 DMA接收
   
    DMA_Cmd(DMA1_Channel5, ENABLE);  //正式驱动DMA传输
    USART_Cmd(USART1, ENABLE);                    //使能串口
    USART_ClearFlag(USART1, USART_FLAG_TC);
   
}
//重新恢复DMA指针
void MYDMA_Enable(DMA_Channel_TypeDef*DMA_CHx)
{
    DMA_Cmd(DMA_CHx, DISABLE );  //关闭USART1 TX DMA1 所指示的通道      
     DMA_SetCurrDataCounter(DMA_CHx,DMA_Rec_Len);//DMA通道的DMA缓存的大小
     DMA_Cmd(DMA_CHx, ENABLE);  //使能USART1 TX DMA1 所指示的通道
}   

//发送len个字节.
//buf:发送区首地址
//len:发送的字节数
void Usart1_Send(u8 *buf,u8 len)
{
    u8 t;
      for(t=0;t<len;t++)        //循环发送数据
    {           
        while(USART_GetFlagStatus(USART1, USART_FLAG_TC) == RESET);      
        USART_SendData(USART1,buf[t]);
    }     
    while(USART_GetFlagStatus(USART1, USART_FLAG_TC) == RESET);        
}
u8 Res;
//串口中断函数
void USART1_IRQHandler(void)                    //串口1中断服务程序
{   

    if(USART_GetITStatus(USART1, USART_IT_IDLE) != RESET)  //接收中断
        {
            DMA_Cmd(DMA1_Channel5, DISABLE);
            
            
         // USART_ReceiveData(USART1);//读取数据 注意:这句必须要,否则不能够清除中断标志位。我也不知道为啥!
          UartReceiveLen = DMA_Rec_Len-DMA_GetCurrDataCounter(DMA1_Channel5);    //算出接本帧数据长度
//            //***********帧数据处理函数************//
//            printf ("The lenght:%d\r\n",UartReceiveLen);
//            printf ("The data:\r\n");
//            Usart1_Send(DMA_Rece_Buf,UartReceiveLen);
//            printf ("\r\nOver! \r\n");
//            //*************************************//
            if(DMA_Rece_Buf[0]==0x02)
            {
                UartSendBuffer[0]=0X02;//固定通讯协议
                UartSendBuffer[1]=0X36;
                UartSendBuffer[2]=0X32;
                UartSendBuffer[3]=0X36;
                UartSendBuffer[4]=0X36;
                UartSendBuffer[5]=0X03;
                UartSendBuffer[6]=0X44;
                UartSendBuffer[7]=0X37;
                for(i=0;i<8;i++)
                {
                USART1->DR=UartSendBuffer[0];
                while((USART1->SR&0X40)==0);//等待发送结束
                }
            }
            if(DMA_Rece_Buf[0] == 0x05)
            {
                USART1->DR=ACK;
                while((USART1->SR&0X40)==0);//等待发送结束
            }
        
        i = USART1->SR;
        i = USART1->DR;
        USART_ClearITPendingBit(USART1, USART_IT_IDLE);         //清除中断标志
        MYDMA_Enable(DMA1_Channel5);                   //恢复DMA指针,等待下一次的接收   
     }

}
2、看PLC和三菱编程软件的通信截图发现一个问题,接收到一帧数据和接收到一个字节好判断,但是怎么实现发送一帧数据 和发送一个字节的区别,看图中显示,是接收到软件发来的一帧数据,但是下位机回复的时候是一个字节一个字节的回复的,是不是要发送完一个字节后进行适量的延时来实现?求不吝赐教,谢谢、

最佳答案

查看完整内容[请看2#楼]

自问自答 结贴。研究了好几天,解决了这个问题。 1、关于通信协议,1位起始位 7位数据位 1位 偶校验 1位停止位 波特率9600这种奇葩的设置,STM32 的串口并不能直接设置,需要换一种思路来变相的达到要求。设置波特率 9600,8位数据位,偶校验,1位停止位。在接收和发送时忽略掉第8位数据,即在接收时 将数据与0x7f进行与运算,发送时 将数据与0x80进行或运算 再发出去,这样就能够正常通信。 html论坛里的一个帖子讲到过这件事情 ...
正点原子逻辑分析仪DL16劲爆上市
回复

使用道具 举报

15

主题

68

帖子

0

精华

中级会员

Rank: 3Rank: 3

积分
289
金钱
289
注册时间
2014-1-2
在线时间
62 小时
 楼主| 发表于 2016-7-13 10:04:14 | 显示全部楼层
自问自答 结贴。研究了好几天,解决了这个问题。
1、关于通信协议,1位起始位 7位数据位 1位 偶校验 1位停止位 波特率9600这种奇葩的设置,STM32 的串口并不能直接设置,需要换一种思路来变相的达到要求。设置波特率 9600,8位数据位,偶校验,1位停止位。在接收和发送时忽略掉第8位数据,即在接收时 将数据与0x7f进行与运算,发送时 将数据与0x80进行或运算 再发出去,这样就能够正常通信。
html论坛里的一个帖子讲到过这件事情
2、我的板子在设置好通信后,与串口通信助手可以正常通信,毫无问题,但是与上位机软件通信就不正常。……这个问题恶心死我了,最后搜到的一条关键信息说电路问题或者晶振不稳定,所以我换了一块板子,正常通信。 所以 问题是解决了,就是板子问题,也许可能是晶振不稳定什么的,至今不理解为什么串口调试助手没事,各种通信测试都正常,换了三菱的软件就不正常,偶尔一两次能正常收到数据,我也是醉了。
回复

使用道具 举报

58

主题

6293

帖子

1

精华

资深版主

Rank: 8Rank: 8

积分
11476
金钱
11476
注册时间
2014-4-1
在线时间
1297 小时
发表于 2016-7-13 10:27:38 | 显示全部楼层
1、貌似这问题与协议无关,是自己程序的接收功能不够完善、有bug?
     先保证自己的接收功能完美,再说。

2、这个不好说,看协议怎么定。但貌似你正在和谐协议,所以现在是个矛盾。

回复

使用道具 举报

15

主题

68

帖子

0

精华

中级会员

Rank: 3Rank: 3

积分
289
金钱
289
注册时间
2014-1-2
在线时间
62 小时
 楼主| 发表于 2016-7-13 10:31:01 | 显示全部楼层
xuande 发表于 2016-7-13 10:27
1、貌似这问题与协议无关,是自己程序的接收功能不够完善、有bug?
     先保证自己的接收功能完美,再说 ...

1,接收功能完善,用串口调试助手测试没问题,每次发送都能进中断并且把数据发出去,但是和三菱编程软件通信就只进一次中断发一次数据。
2,问题我已通过实验解决,发送一个字节后延时delay_ms(10),10毫秒,就是每个字节是一个帧,9毫秒就是所有的字节是一个帧。通过延时实现。
回复

使用道具 举报

15

主题

68

帖子

0

精华

中级会员

Rank: 3Rank: 3

积分
289
金钱
289
注册时间
2014-1-2
在线时间
62 小时
 楼主| 发表于 2016-7-13 10:39:25 | 显示全部楼层
yklstudent 发表于 2016-7-13 10:38
楼主代码问题多多,还是先找找自己程序 的问题吧

额……
回复

使用道具 举报

58

主题

6293

帖子

1

精华

资深版主

Rank: 8Rank: 8

积分
11476
金钱
11476
注册时间
2014-4-1
在线时间
1297 小时
发表于 2016-7-13 10:40:11 | 显示全部楼层

用空闲中断接收,对不对?不好确定。
如果对方在帧之间加延时,你这样接收就对了;
但如果不是呢?

协议是关键。
需要反复观察、摸索、试验。


回复

使用道具 举报

15

主题

68

帖子

0

精华

中级会员

Rank: 3Rank: 3

积分
289
金钱
289
注册时间
2014-1-2
在线时间
62 小时
 楼主| 发表于 2016-7-13 10:45:03 | 显示全部楼层
xuande 发表于 2016-7-13 10:40
用空闲中断接收,对不对?不好确定。
如果对方在帧之间加延时,你这样接收就对了;
但如果不是呢?

我用超时法也试过了,一样的效果…… 我再试试,谢谢
回复

使用道具 举报

2

主题

130

帖子

0

精华

金牌会员

Rank: 6Rank: 6

积分
1870
金钱
1870
注册时间
2011-9-16
在线时间
419 小时
发表于 2016-7-13 21:07:49 | 显示全部楼层
MODBUS RTU?
回复

使用道具 举报

15

主题

68

帖子

0

精华

中级会员

Rank: 3Rank: 3

积分
289
金钱
289
注册时间
2014-1-2
在线时间
62 小时
 楼主| 发表于 2016-7-14 09:38:00 | 显示全部楼层

应该是模拟默认值 吧,上电初始,9600波特率,1位起始位,7位数据位,1位偶校验,1位停止位 加 帧头 帧尾,ASCII码字符,异步通信方式
回复

使用道具 举报

530

主题

11万

帖子

34

精华

管理员

Rank: 12Rank: 12Rank: 12

积分
165377
金钱
165377
注册时间
2010-12-1
在线时间
2111 小时
发表于 2016-7-15 23:55:03 | 显示全部楼层
帮顶
回复

使用道具 举报

15

主题

68

帖子

0

精华

中级会员

Rank: 3Rank: 3

积分
289
金钱
289
注册时间
2014-1-2
在线时间
62 小时
 楼主| 发表于 2016-7-16 10:13:20 | 显示全部楼层

谢原子哥,问题已解决。
回复

使用道具 举报

0

主题

3

帖子

0

精华

新手入门

积分
7
金钱
7
注册时间
2020-5-15
在线时间
1 小时
发表于 2022-4-8 08:37:40 | 显示全部楼层
想问一下楼主,最后是怎么解决的?
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则



关闭

原子哥极力推荐上一条 /2 下一条

正点原子公众号

QQ|手机版|OpenEdv-开源电子网 ( 粤ICP备12000418号-1 )

GMT+8, 2025-2-27 14:17

Powered by OpenEdv-开源电子网

© 2001-2030 OpenEdv-开源电子网

快速回复 返回顶部 返回列表