[单片机芯片] CH32F103 USART1连接接收查询指令后,会中断数据发送

[复制链接]
3144|9
手机看帖
扫描二维码
随时随地手机跟帖
sunicecream|  楼主 | 2021-10-21 16:13 | 显示全部楼层 |阅读模式
采用CH32F103C8T6做下位机,上位机用SSCOM连续向下位机发送查询指令,下位机上传几次数据后就不再上传数据了,只在USART1有此问题,USART2没有这个问题,同样的架构的程序移植到CH32VF103也正常没有出现这个情况,这个是硬件本身的问题吗,可有解决办法,程序随附件

/*******************************************************************************
* [url=home.php?mod=space&uid=288409]@file[/url]    cmd_usart.c
* [url=home.php?mod=space&uid=187600]@author[/url]  MR.Hu
* [url=home.php?mod=space&uid=895143]@version[/url] 0.0.1
* [url=home.php?mod=space&uid=212281]@date[/url]    2021-07-18
* [url=home.php?mod=space&uid=247401]@brief[/url]   命令串口使用
*********************************************************************************************/

/*--------包含的头文件--------------------------------------------------------------------------*/
#include <string.h>
#include "cmd_usart.h"          
#include "global_variables.h"
#include "CRC8_16.h"

/*--------中断声明-----------------------------------------------------------------------------*/

void USART1_IRQHandler(void)        __attribute__((interrupt("WCH-Interrupt-fast")));
void USART2_IRQHandler(void)        __attribute__((interrupt("WCH-Interrupt-fast")));

/*--------全局变量-----------------------------------------------------------------------------*/


/**********************************************************************************************
        * 函数名                : uart_init()
        * 形参变量        :bound-------波特率
        * 返回值                :无
        *        函数功能        :串口1初始化
*********************************************************************************************/
void cmd_uart_init(uint32_t   CMD_USART_Baudrate)
        {
       //-------GPIO端口设置--------------------------------------------------
      GPIO_InitTypeDef                 GPIO_InitStructure;
          USART_InitTypeDef         USART_InitStructure;
          NVIC_InitTypeDef                 NVIC_InitStructure;


          USART_DeInit(USART1);  //复位串口1
          RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1|RCC_APB2Periph_GPIOA, ENABLE);
          //------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);                          //  初始化PA9

          //------USART1_RX     PA.10------------------------------------------------------------------------------
          GPIO_InitStructure.GPIO_Pin     = GPIO_Pin_10;                  //  初始化PA10
          GPIO_InitStructure.GPIO_Mode    = GPIO_Mode_IN_FLOATING;        //  浮空输入
          GPIO_Init(GPIOA, &GPIO_InitStructure);

                // 485收发控制管脚
          RCC_APB2PeriphClockCmd(RS485_RE_GPIO_CLK,ENABLE); //开启按键端口的时钟
          GPIO_InitStructure.GPIO_Pin         = RS485_RE_PIN;
          GPIO_InitStructure.GPIO_Mode         = GPIO_Mode_Out_PP;
          GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
          GPIO_Init(RS485_RE_GPIO_PORT, &GPIO_InitStructure);

          GPIO_ResetBits(RS485_RE_GPIO_PORT,RS485_RE_PIN);                                         //        控制485芯片进入接收模式

                //------Usart1 NVIC 配置-------------------------------------------------------------------------------
          USART_InitStructure.USART_BaudRate              = CMD_USART_Baudrate;                     //  设置波特率
          USART_InitStructure.USART_WordLength            = USART_WordLength_8b;              //  设置字长
          USART_InitStructure.USART_StopBits              = USART_StopBits_1;                 //  1位停止位
          USART_InitStructure.USART_Parity                = USART_Parity_No;                  //  没有校验位
          USART_InitStructure.USART_HardwareFlowControl   = USART_HardwareFlowControl_None;   //  无硬件流控制
          USART_InitStructure.USART_Mode                  = USART_Mode_Tx | USART_Mode_Rx;    //  双工模式


          USART_Init(USART1, &USART_InitStructure);

          // 空闲中断设置
          NVIC_InitStructure.NVIC_IRQChannel                      = USART1_IRQn;
          NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority    = 1;                          //  抢占优先级为1
          NVIC_InitStructure.NVIC_IRQChannelSubPriority           = 1;                          //  子优先级为1
          NVIC_InitStructure.NVIC_IRQChannelCmd                   = ENABLE;                     //  IRQ通道使能
          NVIC_Init(&NVIC_InitStructure);                                                       //  中断优先级初始化

          //    USART_ITConfig(USART1,USART_IT_RXNE,ENABLE);    //  接收中断使能
          USART_ITConfig(USART1,USART_IT_IDLE,ENABLE);          //  串口空闲中断使能
          USART_DMACmd(USART1, USART_DMAReq_Rx, ENABLE);
          USART_Cmd(USART1,ENABLE);
}


/**********************************************************************************************
    * 函数名       : uart_init()
    * 形参变量  :bound-------波特率
    * 返回值       :无
    * 函数功能    :串口1初始化
*********************************************************************************************/
void device_usart_init(uint32_t   DEVICE_USART_Baudrate)
    {
       //-------GPIO端口设置--------------------------------------------------
      GPIO_InitTypeDef      GPIO_InitStructure;
      USART_InitTypeDef     USART_InitStructure;
      NVIC_InitTypeDef      NVIC_InitStructure;


      USART_DeInit(USART2);  //复位串口1
      RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2, ENABLE);
      RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
      //------USART2_TX   PA.2-------------------------------------------------------------------------------
      GPIO_InitStructure.GPIO_Pin     = GPIO_Pin_2;                   //  PA.9
      GPIO_InitStructure.GPIO_Speed   = GPIO_Speed_50MHz;
      GPIO_InitStructure.GPIO_Mode    = GPIO_Mode_AF_PP;              //  复用推挽输出
      GPIO_Init(GPIOA, &GPIO_InitStructure);                          //  初始化PA9

      //------USART1_RX     PA.3------------------------------------------------------------------------------
      GPIO_InitStructure.GPIO_Pin     = GPIO_Pin_3;                  //  初始化PA10
      GPIO_InitStructure.GPIO_Mode    = GPIO_Mode_IN_FLOATING;       //  浮空输入
      GPIO_Init(GPIOA, &GPIO_InitStructure);


        //------Usart1 NVIC 配置-------------------------------------------------------------------------------
      USART_InitStructure.USART_BaudRate              = DEVICE_USART_Baudrate;            //  设置波特率
      USART_InitStructure.USART_WordLength            = USART_WordLength_8b;              //  设置字长
      USART_InitStructure.USART_StopBits              = USART_StopBits_1;                 //  1位停止位
      USART_InitStructure.USART_Parity                = USART_Parity_No;                  //  没有校验位
      USART_InitStructure.USART_HardwareFlowControl   = USART_HardwareFlowControl_None;   //  无硬件流控制
      USART_InitStructure.USART_Mode                  = USART_Mode_Tx | USART_Mode_Rx;    //  双工模式


      USART_Init(USART2, &USART_InitStructure);

      // 空闲中断设置
      NVIC_InitStructure.NVIC_IRQChannel                      = USART2_IRQn;
      NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority    = 1;                          //  抢占优先级为1
      NVIC_InitStructure.NVIC_IRQChannelSubPriority           = 1;                          //  子优先级为1
      NVIC_InitStructure.NVIC_IRQChannelCmd                   = ENABLE;                     //  IRQ通道使能
      NVIC_Init(&NVIC_InitStructure);                                                       //  中断优先级初始化

      //    USART_ITConfig(USART1,USART_IT_RXNE,ENABLE);    //  接收中断使能
      USART_ITConfig(USART2,USART_IT_IDLE,ENABLE);          //  串口空闲中断使能
      USART_DMACmd(USART2, USART_DMAReq_Rx, ENABLE);
      USART_Cmd(USART2,ENABLE);
}



/*******************************************************************************
* Function Name  : DMA1_CH5_Init
* Description    : Initializes Channel3 of DMA1 collection.  DMA1通道5传输参数配置
* Input          : None
* Return         : None
*******************************************************************************/
void DMA1_CH5_USART1_RX_Init(void)
{
  DMA_InitTypeDef   DMA_InitStructure;

  RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);                        //  使能DMA1时钟

  DMA_InitStructure.DMA_PeripheralBaseAddr  = (u32)(&USART1->DATAR);        //  设置源数据地址
  DMA_InitStructure.DMA_MemoryBaseAddr      = (u32) CMD_RX_Buffer;          //  设置目标地址
  DMA_InitStructure.DMA_DIR                 = DMA_DIR_PeripheralSRC;        //  设置传输方向:外设到存储器(此处外设为内部FLASH)
  DMA_InitStructure.DMA_BufferSize          = CMD_RX_Buf_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_PeripheralDataSize_Byte;  //  设置存储器数据单位
  DMA_InitStructure.DMA_Mode                = DMA_Mode_Normal;              //  设置对应DMA工作模式为正常模式
  DMA_InitStructure.DMA_Priority            = DMA_Priority_VeryHigh;        //  DMA1通道5优先级高
  DMA_InitStructure.DMA_M2M                 = DMA_M2M_Disable;              //  使能DMA存储器到存储器的传输方式
  DMA_Init(DMA1_Channel5, &DMA_InitStructure);                              //  根据DMA_InitStruct中指定的参数初始化DMA1通道3

  DMA_Cmd(DMA1_Channel5, ENABLE);                                           //  使能DMA1通道5
}


/*******************************************************************************
* Function Name  : DMA1_CH5_Init
* Description    : Initializes Channel3 of DMA1 collection.  DMA1通道5传输参数配置
* Input          : None
* Return         : None
*******************************************************************************/
void DMA1_CH6_USART2_RX_Init(void)
{
  DMA_InitTypeDef   DMA_InitStructure;

  RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);                        //  使能DMA1时钟

  DMA_InitStructure.DMA_PeripheralBaseAddr  = (u32)(&USART2->DATAR);        //  设置源数据地址
  DMA_InitStructure.DMA_MemoryBaseAddr      = (u32) DEVICE_RX_Buffer;       //  设置目标地址
  DMA_InitStructure.DMA_DIR                 = DMA_DIR_PeripheralSRC;        //  设置传输方向:外设到存储器(此处外设为内部FLASH)
  DMA_InitStructure.DMA_BufferSize          = CMD_RX_Buf_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_PeripheralDataSize_Byte;  //  设置存储器数据单位
  DMA_InitStructure.DMA_Mode                = DMA_Mode_Normal;              //  设置对应DMA工作模式为正常模式
  DMA_InitStructure.DMA_Priority            = DMA_Priority_VeryHigh;        //  DMA1通道5优先级高
  DMA_InitStructure.DMA_M2M                 = DMA_M2M_Disable;              //  使能DMA存储器到存储器的传输方式
  DMA_Init(DMA1_Channel6, &DMA_InitStructure);                              //  根据DMA_InitStruct中指定的参数初始化DMA1通道3

  DMA_Cmd(DMA1_Channel6, ENABLE);                                           //  使能DMA1通道6
}


void DMA1_Init(void)
{
    DMA1_CH5_USART1_RX_Init();
    DMA1_CH6_USART2_RX_Init();
}
/**********************************************************************************************
    * 函数名      :Usart1_Send()
    * 形参变量  :无
    * 返回值      :无
    * 函数功能  :串口1发送程序
*********************************************************************************************/
void Usart1_Send(uint8_t *buf,  uint16_t len)
{
    uint16_t t;
    RS485_TX_EN();
          for(t=0;t<len;t++)                //        循环发送数据
          {
                while(USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET);        //  TXE 弹夹为空
                USART_SendData(USART1,buf[t]);
          }

        while(USART_GetFlagStatus(USART1, USART_FLAG_TC) == RESET);             //  TC,弹夹为空,枪膛也为空
          RS485_RX_EN();
}


/*******************************************************************************
    * 函数名      :Usart2_Send()
    * 形参变量  :无
    * 返回值      :无
    * 函数功能  :串口2发送程序
*******************************************************************************/
void Usart2_Send(uint8_t *buf,  uint16_t len)
{
    uint16_t t;

    for(t=0;t<len;t++)      //  循环发送数据
      {
        while(USART_GetFlagStatus(USART2, USART_FLAG_TXE) == RESET);
        USART_SendData(USART2,buf[t]);
      }

    while(USART_GetFlagStatus(USART2, USART_FLAG_TC) == RESET);
}

/**********************************************************************************************
    * 函数名       :USARTx_SendByte()
    * 形参变量   :pUSARTx,可以是USART1、USART2、USART3
    *          data,要发送的字节
    * 返回值       :无
    *函数功能      :串口发送字符串
*********************************************************************************************/
void USARTx_SendByte(USART_TypeDef* pUSARTx, uint8_t data)
{
    while(USART_GetFlagStatus(pUSARTx, USART_FLAG_TXE) == RESET);
    USART_SendData(pUSARTx, data);
    while(USART_GetFlagStatus(USART2, USART_FLAG_TC) == RESET);
}
/**********************************************************************************************
    * 函数名       :USARTx_SendStr()
    * 形参变量   :pUSARTx:  可以是USART1、USART2、USART3
    *         *str   :  字符串
    * 返回值       :无
    *函数功能      :串口发送字符串
*********************************************************************************************/
void USARTx_SendStr(USART_TypeDef* pUSARTx, char *str)
{
  uint8_t      i = 0;

  RS485_TX_EN();
  do
    {
       USARTx_SendByte(pUSARTx, *(str+i));
       i++;
    }while(*(str+i) != '\0');

  while(USART_GetFlagStatus(pUSARTx, USART_FLAG_TC) == RESET);
  RS485_RX_EN();
}


void USART2_SendStr(char *str)
{
  uint8_t      i = 0;

  do
    {
       USARTx_SendByte(USART2, *(str+i));
       i++;
    }while(*(str+i) != '\0');

  while(USART_GetFlagStatus(USART2, USART_FLAG_TC) == RESET);
}

/**********************************************************************************************
        * 函数名         :USART1_IRQHandler()
        * 形参变量            :无
        * 返回值            :无
        *函数功能            :串口1中断服务程序
*********************************************************************************************/
void USART1_IRQHandler(void)
        {
          if(USART_GetITStatus(USART1, USART_IT_IDLE) != RESET)
                {
              DMA_Cmd(DMA1_Channel5,DISABLE);                                                   //  关闭DMA传输
              CMD_USART_Rec_Cnt =  CMD_RX_Buf_Len - DMA_GetCurrDataCounter(DMA1_Channel5);      //  获取串口接收的数据数量

              Receive_one_fame_OK = 1;      //  1帧数据完成接收
              //-----------------------------------------------------------------------------------------------------------------------------------------//
              // USART1->STATR;        //  读状态寄存器
              // USART1->DATAR;        //  再读数据寄存器,此操作用于清除空闲中断标志位


              DMA_SetCurrDataCounter(DMA1_Channel5,CMD_RX_Buf_Len);     //  重新设置传输的数据数量
              DMA_Cmd(DMA1_Channel5,ENABLE);                            //  开启DMA传输

              USART_ReceiveData(USART1);                                //  读取一次数据,不然会一直进中断
              USART_ClearFlag(USART1,USART_FLAG_IDLE);                  //  清除串口空闲中断标志位
                }
}


/**********************************************************************************************
    * 函数名         :USART1_IRQHandler()
    * 形参变量      :无
    * 返回值       :无
    *函数功能       :串口1中断服务程序
*********************************************************************************************/
void USART2_IRQHandler(void)
    {
        uint16_t CRC_calchkval;

      if(USART_GetITStatus(USART2, USART_IT_IDLE) != RESET)
        {
          DMA_Cmd(DMA1_Channel6,DISABLE);                                                   //  关闭DMA传输
          DEVICE_USART_Rec_Cnt =  CMD_RX_Buf_Len - DMA_GetCurrDataCounter(DMA1_Channel6);   //  获取串口接收的数据数量

          DEVICE_USART_Receive_complete = 1;      //  1帧数据完成接收
          //--------------------------------------------------------------------------------//
          CMD_TX_Buffer[0]   =HEAD_FRAME_HI;                                                //  帧头高位
          CMD_TX_Buffer[1]   =HEAD_FRAME_LO;                                                //  帧头低位
          CMD_TX_Buffer[2]   =BOARD_ADDRESS;                                                //  电路模块地址
          CMD_TX_Buffer[3]  = functional_unit;                                              //  功能单元,同一地址的电路模块中的某个器件,00表示回复数据为版本号
          CMD_TX_Buffer[4]  = functional_code;                                              //  功能码
          CMD_TX_Buffer[5]   =((uint8_t)((DEVICE_USART_Rec_Cnt & 0xFF00)>>8));              //  数据长度
          CMD_TX_Buffer[6]   =((uint8_t)( DEVICE_USART_Rec_Cnt & 0x00FF));                  //  数据长度

          memcpy(CMD_TX_Buffer+7,DEVICE_RX_Buffer,DEVICE_USART_Rec_Cnt);                    //  数据保存在第二个缓冲区
          CRC_calchkval = MODBUS_CRC16(CMD_TX_Buffer, 7+DEVICE_USART_Rec_Cnt);
          CMD_TX_Buffer[7+DEVICE_USART_Rec_Cnt] = ((uint8_t)(( CRC_calchkval & 0xFF00)>>8));//  校验码保存到待发送的缓存里
          CMD_TX_Buffer[8+DEVICE_USART_Rec_Cnt] = ((uint8_t)(  CRC_calchkval & 0x00FF)    );
          Usart1_Send(CMD_TX_Buffer,9+DEVICE_USART_Rec_Cnt);

          DMA_SetCurrDataCounter(DMA1_Channel6,CMD_RX_Buf_Len);     //  重新设置传输的数据数量
          DMA_Cmd(DMA1_Channel6,ENABLE);                            //  开启DMA传输

          USART_ReceiveData(USART2);                                //  读取一次数据,不然会一直进中断
          USART_ClearFlag(USART2,USART_FLAG_IDLE);                  //  清除串口空闲中断标志位
        }
}
















CH32F103C8T6_debug.rar

532.87 KB, 下载次数: 1

使用特权

评论回复
LIzs6| | 2021-10-21 16:45 | 显示全部楼层
看了一下,怀疑问题可能是没有清除空闲中断标志位导致的,空闲中断标志位的清除方法应该先读状态寄存器,再读数据寄存器,然而这一段被你注释掉了

使用特权

评论回复
sunicecream|  楼主 | 2021-10-21 17:36 | 显示全部楼层
LIzs6 发表于 2021-10-21 16:45
看了一下,怀疑问题可能是没有清除空闲中断标志位导致的,空闲中断标志位的清除方法应该先读状态寄存器,再 ...

之胶尝试过加上的,发现没用,就注释掉了,刚重新试了一下,确实不行。上位机间隔50ms发送查询,返回几次结果之后就停掉了

使用特权

评论回复
RISCVLAR| | 2021-10-21 20:07 | 显示全部楼层
sunicecream 发表于 2021-10-21 17:36
之胶尝试过加上的,发现没用,就注释掉了,刚重新试了一下,确实不行。上位机间隔50ms发送查询,返回几次 ...

是类似于下面截图那种通过串口1发送指令,单片机根据指令通过串口1返回相关信息么?是通过串口1空闲中断进行数据收发处理么? Snipaste_2021-10-21_20-04-38.jpg

使用特权

评论回复
sunicecream|  楼主 | 2021-10-21 20:45 | 显示全部楼层
RISCVLAR 发表于 2021-10-21 20:07
是类似于下面截图那种通过串口1发送指令,单片机根据指令通过串口1返回相关信息么?是通过串口1空闲中断 ...

我是用空闲中断+DMA接收不定长的数据帧,空闲中断接收完一帧数据后,在空闲中断内置位一个接收帧完成标志位和恢复DMA接收的操作就出中断了,帧命令解析都是在主函数内完成的。比如我发送个命令A5 5A 30 00 02 00 00 来查询硬件的版本信息,下位机解析完命令后就回类似的命令。现在的问题是我用定时发送,间隔50ms,下位机返回几次信息后,就不再返回了。同样的程序,在STM32上运行,是没有任何问题的,RISV的CH32V103上边运行也没有问题。

使用特权

评论回复
zchong| | 2021-10-22 07:38 | 显示全部楼层
这种情况就头痛医头,脚痛医脚就行了,调试看看是哪个标志出问题了呗

使用特权

评论回复
LIzs6| | 2021-10-22 10:23 | 显示全部楼层
sunicecream 发表于 2021-10-21 17:36
之胶尝试过加上的,发现没用,就注释掉了,刚重新试了一下,确实不行。上位机间隔50ms发送查询,返回几次 ...

这边有个串口空闲中断DMA例程,对比看一下吧

串口空闲中断-DMA.zip

593.46 KB, 下载次数: 1

使用特权

评论回复
cainiao518| | 2021-10-22 10:37 | 显示全部楼层
不用DMA试试?

使用特权

评论回复
ayb_ice| | 2021-10-22 13:16 | 显示全部楼层
肯定程序问题,把上位机发送的间隔搞大些再测试

使用特权

评论回复
sunicecream|  楼主 | 2021-10-22 13:45 | 显示全部楼层
本帖最后由 sunicecream 于 2021-10-22 13:51 编辑

找到原因了,是硬件问题,出于兼容性的原因,跟别的串口有连接,断开与该芯片连接后,问题没有出现。只是同样的连接,用STM32F103和CH32V103是没有这样的问题的。估计是芯片内部某种原因。程序是没有问题的。这里感谢大家的回复!

使用特权

评论回复
您需要登录后才可以回帖 登录 | 注册

本版积分规则