找回密码
 立即注册

QQ登录

只需一步,快速开始

搜索
查看: 1078|回复: 4
打印 上一主题 下一主题
收起左侧

STC32G单片机的串口DMA问题

[复制链接]
跳转到指定楼层
楼主
ID:929517 发表于 2023-9-8 09:13 来自手机 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
在B站上布丁橘长有一个STC32G单片机利用串口DMA收发不定长度数据视频,我下载了例程,这个DMA收发还是需要串口中断的,也是每个字节中断一次,我想问问,1.这个中断和cpu自己中断收发有区别吗?每次中断CPU不都要停下来压栈什么的,收完恢复现场吗?2.例程有个putchar函数,但在主程序没有看到调用啊?

  1. //        @布丁橘长 2023/04/26
  2. //         串口1DMA接收不定长度数据示例:利用串口1中断,进行超时判定,实现串口1DMA接收不定长度数据
  3. //        实验效果:PC端发送不定长度数据给MCU,MCU接收完成后,原样返回给PC(代码缓冲区设置为256字节,可以根据需要更改)
  4. //  程序设置了2种发送模式, 实现DMA不定长度数据超时接收,printf串口打印该长度数据,以及固定长度串口DMA发送
  5. //                                                                                                  发送模式1:接收数据长度小于256字节,使用超时判定,接收不定长度数据,然后返回该长度数据给PC端
  6. //                                                                                                  发送模式2:接收数据长度等于256字节,使用串口DMA,将数据原样返回给PC
  7. //  串口1使用默认引脚P3.0(RxD) P3.1(TxD)
  8. //        实验开发板:STC32G12K128屠龙刀三.1 主频@22.1184MHz

  9. #include <STC32G.H>
  10. #include <stdio.h>
  11. #include "config.h"

  12. #define BRT (65536 - (MAIN_Fosc / 115200+2) / 4)                // 加 2 操作是为了让 Keil 编译器,自动实现四舍五入运算
  13.                                                                                                                                                                                                                 // 波特率115200
  14. #define DMA_AMT_LEN 255                         // DMA传输总字节(AMT+1) 255+1=256字节
  15.                                                                                                                                                                                                                                                                                                                                                                                                         
  16. u8 xdata DMABuffer[256];                        // 数据存放在XRAM(XDATA区域),需要使用关键字xdata
  17. bit        DmaTxFlag;                                                                // 发送完成标志
  18. bit        DmaRxFlag;                                                                // 接收完成标志
  19. bit B_1ms;                                                                                // 1毫秒标志
  20. bit busy;                                                                                        // 串口忙标志
  21. u8 Rx_cnt;                                                                                // Rx接收计数
  22. u8 RX_TimeOut;                                                                // 串口接收超时计数        
  23. u8 i;

  24. void sysini(void);                                                // STC32初始化设置
  25. void Uart1Init();                                                        // UART1初始化
  26. void DMA_Config();                                                // DMA初始化
  27. void Timer0_Init(void);                                // 定时器0初始化
  28. void UartPutc(u8 dat);                                // 串口发送字符函数
  29. char putchar(char c);                                        // 重构的putchar函数,用于printf函数串口打印

  30. void main(void)
  31. {
  32.         sysini();                                                                                // STC32初始化设置
  33.         Timer0_Init();                                                        // 定时器0初始化
  34.         Uart1Init();                                                                // 串口1初始化
  35.         DMA_Config();                                                                // 串口1DAM初始化
  36.         EA = 1;                                                                                        // 使能EA总中断
  37.         
  38.         DmaTxFlag = 0;                                                        // 清零发送完成标志
  39.         DmaRxFlag = 0;                                                        // 清零接收完成标志
  40.         while (1)
  41.         {
  42.                 if((DmaTxFlag) && (DmaRxFlag))        // 当发送和接收完成标志均为1时,表示空闲
  43.                 {
  44.                         Rx_cnt = 0;                                                        // 清零接收计数
  45.                         RX_TimeOut = 0;                                // 清零接收超时计数
  46.                         DmaTxFlag = 0;                                        // 清零发送完成标志
  47.                         DMA_UR1T_CR = 0xc0;                        // bit7 1:使能 UART1_DMA, bit6 1:开始 UART1_DMA 自动发送
  48.                         DmaRxFlag = 0;                                        // 清零接收完成标志
  49.                         DMA_UR1R_CR = 0xa1;                        // bit7 1:使能 UART1_DMA, bit5 1:开始 UART1_DMA 自动接收, bit0 1:清除 FIFO
  50.                 }
  51.                 if(B_1ms)                                                                 //1ms 到
  52.                 {
  53.                         B_1ms = 0;
  54.                         if(RX_TimeOut > 0)                        // 超时计数
  55.                         {
  56.                                 if(--RX_TimeOut == 0)                                                // 接收超时计数
  57.                                 {
  58.                                         DMA_UR1R_CR = 0x00;                                         // 关闭 UART1_DMA
  59.                                         printf("\r\n接收超时,以下是已接收到的数\xFD据:\r\n");         // UART1 发送 一个字符串,\xFD用于补全汉字‘数’的编码,防止出现乱码
  60.                                         for(i=0;i<Rx_cnt;i++) printf("%bc",DMABuffer[i]);                // 将接收到的数据,发送给PC端
  61.                                         printf("\r\n");                                                                // 数据发送完成后,发送回车、换行符
  62.                                         Rx_cnt = 0;                                                                                // 清零接收计数
  63.                                         DMA_UR1R_CR = 0xa1;                                         //bit7 1: 使能UART1_DMA,bit5 1: 开始 UART1_DMA 自动接收,bit0 1: 清除 FIFO
  64.                                 }
  65.                         }
  66.                 }
  67.         }
  68. }
  69. void sysini()
  70. {
  71.         EAXFR = 1;                                                                         // 使能访问 XFR
  72.         CKCON = 0x00;                                                         // 设置外部数据总线速度为最快
  73.         WTST = 0x00;                                                                // 设置程序代码等待参数,等待时间为0个时钟,CPU执行程序速度最快

  74.         P0M1 = 0x00;P0M0 = 0x00;                // 设置P0口为准双向口模式 //00:准双向口 01:推挽输出 10:高阻输入 11:开漏输出
  75.         P1M1 = 0x00;P1M0 = 0x00;                // 设置P1口为准双向口模式 //00:准双向口 01:推挽输出 10:高阻输入 11:开漏输出
  76.         P2M1 = 0x00;P2M0 = 0x00;                // 设置P2口为准双向口模式 //00:准双向口 01:推挽输出 10:高阻输入 11:开漏输出
  77.         P3M1 = 0x00;P3M0 = 0x00;                // 设置P3口为准双向口模式 //00:准双向口 01:推挽输出 10:高阻输入 11:开漏输出
  78.         P4M1 = 0x00;P4M0 = 0x00;                // 设置P4口为准双向口模式 //00:准双向口 01:推挽输出 10:高阻输入 11:开漏输出
  79.         P5M1 = 0x00;P5M0 = 0x00;                // 设置P5口为准双向口模式 //00:准双向口 01:推挽输出 10:高阻输入 11:开漏输出
  80.         P6M1 = 0x00;P6M0 = 0x00;                // 设置P6口为准双向口模式 //00:准双向口 01:推挽输出 10:高阻输入 11:开漏输出
  81.         P7M1 = 0x00;P7M0 = 0x00;                // 设置P7口为准双向口模式 //00:准双向口 01:推挽输出 10:高阻输入 11:开漏输出
  82. }
  83. void Timer0_Isr(void) interrupt 1
  84. {
  85.         B_1ms = 1;                                                                        // 1毫秒标志
  86. }
  87. void Timer0_Init(void)                                //1毫秒@22.1184MHz
  88. {
  89.         AUXR |= 0x80;                                                                //定时器时钟1T模式
  90.         TMOD &= 0xF0;                                                                //设置定时器模式
  91.         TL0 = 0x9A;                                                                        //设置定时初始值
  92.         TH0 = 0xA9;                                                                        //设置定时初始值
  93.         TF0 = 0;                                                                                //清除TF0标志
  94.         TR0 = 1;                                                                                //定时器0开始计时
  95.         ET0 = 1;                                                                                //使能定时器0中断
  96. }
  97. void Uart1Init()                                                        // UART1初始化
  98. {
  99.         SCON = 0x50;                                                                // 模式1(8位数据)、接收使能
  100.         T2L = BRT;                                                
  101.         T2H = BRT >> 8;                                                        // 波特率对应的重装载值
  102.         S1BRT = 1;                                                                        // 定时器2做波特率发生器
  103.         T2x12 = 1;                                                                        // 1T模式
  104.         T2R = 1;                                                                                // 启动定时器2
  105.         ES = 1;                                                                                        // 使能串口1中断
  106. }
  107. void UartPutc(u8 dat)                                        // 串口发送字符函数
  108. {
  109.         busy = 1;                                                                                // 发送前,将忙标志busy置1
  110.         SBUF = dat;                                                                        // 需要发送的数据dat送入SBUF
  111.         while(busy);                                                                // 等待发送完成
  112. }
  113. char putchar(char c)                                        // 重构的putchar函数,用于printf函数串口打印
  114. {
  115.         UartPutc(c);                                                                // 调用UartPutc串口发送字符函数
  116.         return c;
  117. }
  118. void UART1_Isr (void) interrupt 4
  119. {
  120.         if(RI)                                                                                        // 接收完成标志置1时
  121.         {
  122.                 RI = 0;                                                                                // 清零接收完成标志
  123.                 Rx_cnt++;                                                                        // 接收计数+1
  124.                 if(Rx_cnt > DMA_AMT_LEN) Rx_cnt = 0;                // 接收计数大于等于DMA缓冲区长度,清零接收计数
  125.                 RX_TimeOut = 5;                                         // 如果 5ms 没收到新的数据,判定一串数据接收完毕
  126.         }
  127.         if(TI)                                                                                        // 发送标志置1时
  128.         {
  129.                 TI = 0;                                                                                // 清零发送完成标志
  130.                 busy = 0;                                                                        // 清零串口忙标志
  131.         }
  132. }
  133. void DMA_Config(void)
  134. {
  135.         DMA_UR1T_CFG = 0x80;                                // bit7 1:使能串口1DMA发送中断
  136.         DMA_UR1T_STA = 0x00;                                // 清零串口1DMA发送完成中断标志、清零数据覆盖中断标志
  137.         DMA_UR1T_AMT =  DMA_AMT_LEN;                                                                // 设置传输总字节数(低8位):n+1
  138.         DMA_UR1T_AMTH = DMA_AMT_LEN >> 8;                                                // 设置传输总字节数(高8位):n+1
  139.         DMA_UR1T_TXAH = (u8)((u16)&DMABuffer >> 8);        // 设置传输数据的源地址,高8位
  140.         DMA_UR1T_TXAL = (u8)((u16)&DMABuffer);                        // 设置传输数据的源地址,低8位
  141.         DMA_UR1T_CR = 0xc0;                                        // bit7 1:使能串口1DMA发送, bit6 1:开始DMA自动发送

  142.         DMA_UR1R_CFG = 0x80;                                // bit7 1:使能串口1DMA接收中断
  143.         DMA_UR1R_STA = 0x00;                                // 清零串口1DMA接收完成中断标志、清零数据丢弃中断标志
  144.         DMA_UR1R_AMT =  DMA_AMT_LEN;                                                                // 设置传输总字节数(低8位):n+1
  145.         DMA_UR1R_AMTH = DMA_AMT_LEN >> 8;                                                // 设置传输总字节数(高8位):n+1
  146.         DMA_UR1R_RXAH = (u8)((u16)&DMABuffer >> 8);        // 设置传输数据的目标地址,高8位
  147.         DMA_UR1R_RXAL = (u8)((u16)&DMABuffer);                        // 设置传输数据的目标地址,低8位
  148.         DMA_UR1R_CR = 0xa1;                                        //bit7 1:使能串口1DMA接收, bit5 1:开始DMA自动接收, bit0 1:清除 FIFO
  149. }
  150. void UART1_DMA_Interrupt(void) interrupt 13                // 串口DMA中断号大于31,借用13号保留中断中转
  151. {                                                                                                                                                                                        // 详情参照布丁橘长-STC32系列视频第36期,或STC32手册第5.9章节
  152.         if (DMA_UR1T_STA & 0x01)                // 发送完成中断标志为1时
  153.         {
  154.                 DMA_UR1T_STA &= ~0x01;                // 清零发送完成中断标志
  155.                 DmaTxFlag = 1;                                                // 发送完成标志置1
  156.         }
  157.         if (DMA_UR1T_STA & 0x04)                // 数据覆盖中断标志为1时
  158.         {
  159.                 DMA_UR1T_STA &= ~0x04;                // 清零数据覆盖中断标志
  160.         }
  161.         if (DMA_UR1R_STA & 0x01)                // 接收完成中断标志为1时
  162.         {
  163.                 DMA_UR1R_STA &= ~0x01;                // 清零接收完成中断标志
  164.                 DmaRxFlag = 1;                                                // 接收完成标志置1
  165.         }
  166.         if (DMA_UR1R_STA & 0x02)                // 数据丢弃中断标志为1时
  167.         {
  168.                 DMA_UR1R_STA &= ~0x02;                // 清零数据丢弃中断标志
  169.         }
  170. }
复制代码

分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友
收藏收藏 分享淘帖 顶 踩
回复

使用道具 举报

沙发
ID:1034262 发表于 2023-9-8 11:22 | 只看该作者
DMA串口发送不需要串口中断的,给定数据源首地址,给定发送长度,启动DMA发送,直到DMA发送完成,标志提示(也可以允许DMA发送完成中断)。
回复

使用道具 举报

板凳
ID:929517 发表于 2023-9-8 16:47 来自手机 | 只看该作者
coody_sz 发表于 2023-9-8 11:22
DMA串口发送不需要串口中断的,给定数据源首地址,给定发送长度,启动DMA发送,直到DMA发送完成,标志提示 ...

这个是不定数据长度的收发,利用超时判断接收完成。但是我不理解接收要进入中断
回复

使用道具 举报

地板
ID:1093023 发表于 2023-9-9 00:18 | 只看该作者
      利用串口DMA收发不定长度数据。发送通常不考虑。主要难度在于接收。我长期做STM32的开发。以STM32 为例说明,供你参考。用串口接收不定长数据,开启接收空闲中断+DMA接收,优于开启接收中断+DMA接收。
      接收中断是指每接收一个字节数据就进入中断,设置一个变量,每进入一次中断就自加1,判断长时间不进入中断,就表明接收完成,通过DMA通道把串口接收寄存器与某个内存数组联系起来,串口接收的数据直接转存给内存数组元素。内存数组元素自动递增。这种方法效率低,因为频繁地进入中断,会影响其它代码或其它中断程序的运行。
      而接收空闲中断+DMA接收是指串口在接收数据时不进入中断,接收的数据通过DMA通道自动转存给指定的内存数组元素。当串口长时间不再收到数据时,就会进入空闲中断,这表明接收完成。在中断程序中可以通过调用库函数并通过计算,得到接收了多少个字节的数据。这种方式效率高,一般不会影响其它代码或其它中断程序的运行。

评分

参与人数 1黑币 +40 收起 理由
admin + 40 回帖助人的奖励!

查看全部评分

回复

使用道具 举报

5#
ID:929517 发表于 2023-9-9 05:00 来自手机 | 只看该作者
zhangqi_12345 发表于 2023-9-9 00:18
利用串口DMA收发不定长度数据。发送通常不考虑。主要难度在于接收。我长期做STM32的开发。以STM32 为 ...

谢谢,我再研究下,多谢前辈
回复

使用道具 举报

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

本版积分规则

手机版|小黑屋|51黑电子论坛 |51黑电子论坛6群 QQ 管理员QQ:125739409;技术交流QQ群281945664

Powered by 单片机教程网

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