- 打卡等级:以坛为家II
- 打卡总天数:443
- 最近打卡:2025-05-09 10:10:53
已绑定手机
金牌会员
- 积分
- 1619
|
发表于 2024-6-4 22:30:10
|
显示全部楼层
本帖最后由 未元星系 于 2024-6-4 22:35 编辑
自学开天斧第⑩课:
学习内容:1、串口DMA发送与接收
学习简介:串口通信是单片机和电脑通信的常用方法,但当需要一次发送的数据总量很大时,就会严重占用主程序时间,为了避免这个问题,我们可以将需要串口发送的数据存到DMA里发送,这样会提高单片机运行效率,减少主函数占用。串口DMA一次最多可以发送或接收256字节数据(可自行调整为小于256字节的数据)
实验项目:单片机向串口发送256字节数据->P2口灯亮灭两次->检测DMA输入(若有输入则打印在串口上)
实验重点:为体现DMA使用对主程序效率的影响,串口波特率设置为9600
程序代码:
#include <STC8H.H>
#include <stdio.h>
#define MAIN_Fosc 24000000L
#define BRT (65536 - (MAIN_Fosc / 9600+2) / 4)
#define DMA_AMT_LEN 255 // DMA传输总字节(AMT+1) 9999+1=10000字节
unsigned char xdata DMABuffer_send[256]; // DMA发送缓存 DMA数据存放在XRAM(XDATA区域),需要使用关键字xdata
unsigned char xdata DMABuffer_review[256]; //DMA接收缓存
unsigned int j,k;
bit busy;
bit B_1ms; // 1毫秒标志
bit DmaTxFlag; // 发送完成标志
bit DmaRxFlag; // 接收完成标志
//unsigned int numb;
unsigned char Rx_cnt; // Rx接收计数
unsigned char RX_TimeOut;
void Delay_ms(unsigned int ms)
{
unsigned int a;
do{
a = MAIN_Fosc / 10000;
while(--a);
}while(--ms);
}
void Timer0_Init(void) // 1毫秒@24MHz
{
AUXR |= 0x80; //设置定时器为1T模式,即不分频
TMOD &= 0xF0; //保留高四位(定时器1),低四位全为0:设置定时器/计数器0为定时器、GATE置0、16位自动重装模式
TL0 = 0x3F; //初始值65535 - 24000 = 41535 = 1010 0010 0011 1111,即高8位为0xA2,低8位为0x3F
TH0 = 0xA2; //
TF0 = 0; //清零TF0中断标志位
TR0 = 1; //定时器开始计时
ET0 = 1; //开启定时器0中断
}
void Blink()
{unsigned char i;for(i = 0;i < 4;i++){P2 = ~P2;Delay_ms(100);}} //P2口灯亮灭两次
void Uart1Init() // UART1初始化
{
P_SW1 &= 0x3F; //设置串口1引脚为P3.0,P3.1
SCON = 0x50; // 模式1(8位数据)、接收使能
PCON |= 0x00; //设置波特率不加倍,关闭帧错检测功能
T2L = BRT;
T2H = BRT >> 8; // 波特率对应的重装载值
AUXR |= 0x15;
busy = 0; // 清零忙标志
}
void UartPutc(unsigned char dat)
{
busy = 1;
SBUF = dat;
while(!TI);
TI = 0;
busy = 0;
}
char putchar(char c)
{
UartPutc(c);
return c;
}
void DMA_Config(void)
{
DMA_UR1T_CFG = 0x80; // bit7 1:使能串口1DMA发送中断
DMA_UR1T_STA = 0x00; // 清零串口1DMA发送完成中断标志、清零数据覆盖中断标志
DMA_UR1T_AMT = DMA_AMT_LEN; // 设置传输总字节数:n+1
DMA_UR1T_TXAH = (unsigned char)((unsigned int)&DMABuffer_send >> 8); // 设置传输数据的源地址,高8位
DMA_UR1T_TXAL = (unsigned char)((unsigned int)&DMABuffer_send); // 设置传输数据的源地址,低8位
DMA_UR1T_CR = 0xc0; // bit7 1:使能串口1DMA发送, bit6 1:开始DMA自动发送
DMA_UR1R_CFG = 0x80; // bit7 1:使能串口1DMA接收中断
DMA_UR1R_STA = 0x00; // 清零串口1DMA接收完成中断标志、清零数据丢弃中断标志
DMA_UR1R_AMT = DMA_AMT_LEN; // 设置传输总字节数:n+1
DMA_UR1R_RXAH = (unsigned char)((unsigned int)&DMABuffer_review >> 8); // 设置传输数据的目标地址,高8位
DMA_UR1R_RXAL = (unsigned char)((unsigned int)&DMABuffer_review); // 设置传输数据的目标地址,低8位
DMA_UR1R_CR = 0xa1; //bit7 1:使能串口1DMA接收, bit5 1:开始DMA自动接收, bit0 1:清除 FIFO
}
void main()
{
P3M0 = 0x00; P3M1 = 0x00;
P2M0 = 0x00; P2M1 = 0x00;
P_SW2 |= 0x80; //使能XFR
ES = 1; // 使能串口1中断
EA = 1; // 使能EA总中断
Timer0_Init();
Uart1Init();
DMA_Config();
DmaTxFlag = 0; // 清零发送完成标志
DmaRxFlag = 0; // 清零接收完成标志
for(k = 0;k < 256; k++) // 200个字符
{
DMABuffer_send[k] = k%128; // 200个字符,为128个ASCII字符
}
while (1)
{
DmaTxFlag = 1;
DmaRxFlag = 1;
if((DmaTxFlag) && (DmaRxFlag)) // 当发送和接收完成标志均为1时,表示空闲
{
DmaTxFlag = 0; // 清零发送完成标志
DMA_UR1T_CR = 0xc0; // bit7 1:使能 UART1_DMA, bit6 1:开始 UART1_DMA 自动发送
DmaRxFlag = 0; // 清零接收完成标志
DMA_UR1T_CR = 0xa1; // bit7 1:使能 UART1_DMA, bit5 1:开始 UART1_DMA 自动接收, bit0 1:清除 FIFO
}
Blink();
DmaTxFlag = 0; // 清零发送完成标志
DmaRxFlag = 0; // 清零接收完成标志
if((DmaTxFlag) && (DmaRxFlag)) // 当发送和接收完成标志均为1时,表示空闲
{
Rx_cnt = 0; // 清零接收计数
RX_TimeOut = 0; // 清零接收超时计数
DmaTxFlag = 0; // 清零发送完成标志
DMA_UR1T_CR = 0xc0; // bit7 1:使能 UART1_DMA, bit6 1:开始 UART1_DMA 自动发送
DmaRxFlag = 0; // 清零接收完成标志
DMA_UR1R_CR = 0xa1; // bit7 1:使能 UART1_DMA, bit5 1:开始 UART1_DMA 自动接收, bit0 1:清除 FIFO
}
if(B_1ms) //1ms 到
{
B_1ms = 0;
if(RX_TimeOut > 0) // 超时计数
{
if(--RX_TimeOut == 0) // 接收超时计数
{
DMA_UR1R_CR = 0x00; // 关闭 UART1_DMA
printf("\n以下是已接收到的数\xFD据:\r\n"); // UART1 发送 一个字符串,\xFD用于补全汉字‘数’的编码,防止出现乱码
for(j=0;j<Rx_cnt;j++) printf("%bc",DMABuffer_review[j]); // 将接收到的数据,发送给PC端
printf("\r\n"); // 数据发送完成后,发送回车、换行符
Rx_cnt = 0; // 清零接收计数
DMA_UR1R_CR = 0xa1; //bit7 1: 使能UART1_DMA,bit5 1: 开始 UART1_DMA 自动接收,bit0 1: 清除 FIFO
}
}
}
}
}
void Timer0_Routine(void) interrupt 1
{
B_1ms = 1; // 1毫秒标志
}
void Uart1Isr() interrupt 4
{
if (TI == 1){TI = 0;busy = 0;}
if (RI == 1)
{
RI = 0;
Rx_cnt++; // 接收计数+1
if(Rx_cnt > DMA_AMT_LEN) {Rx_cnt = 0;} // 接收计数大于等于DMA缓冲区长度,清零接收计数
RX_TimeOut = 5; // 如果 5ms 没收到新的数据,判定一串数据接收完毕x = 1;}
}
}
void UART1_DMA_Interrupt(void) interrupt 13 // 串口DMA中断号大于31,借用13号保留中断中转
{
if (DMA_UR1T_STA & 0x01) // 发送完成中断标志为1时
{
DMA_UR1T_STA &= ~0x01; // 清零发送完成中断标志
DmaTxFlag = 1; // 发送完成标志置1
}
if (DMA_UR1T_STA & 0x04) // 数据覆盖中断标志为1时
{
DMA_UR1T_STA &= ~0x04; // 清零数据覆盖中断标志
}
if (DMA_UR1R_STA & 0x01) // 接收完成中断标志为1时
{
DMA_UR1R_STA &= ~0x01; // 清零接收完成中断标志
DmaRxFlag = 1; // 接收完成标志置1
}
if (DMA_UR1R_STA & 0x02) // 数据丢弃中断标志为1时
{
DMA_UR1R_STA &= ~0x02; // 清零数据丢弃中断标志
}
}
实验现象:单片机向串口发送256字节数据->P2口灯亮灭两次->检测DMA输入(若有输入则打印在串口上),串口发送接收数据不打断P2口LED按其频率闪烁
|
|