我已获取到全部所需信息。现在为您编写完整的 DMA 串口1收发程序。
说明
- 芯片:STC8H1K17T(TSSOP20 封装)
- 主频:22.1184MHz
- 串口1:RxD = P3.6,TxD = P3.7(通过 P_SW1 的 S1_S[1:0]=01 切换)
- 功能:DMA 接收 + DMA 发送。PC 发来的数据由 DMA 自动存入缓冲区(接收超时判断),超时后把收到的数据通过 DMA 自动回发。
- 波特率:115200(用定时器1模式0做波特率发生器,1T 模式)
说明:STC8H 系列是 8 位机,使用 Keil C51 编译。以下为单文件完整程序。
/************************************************************
* 文件名 : UART1_DMA_STC8H1K17T.c
* 芯片 : STC8H1K17T (TSSOP20封装)
* 主频 : 22.1184MHz
* 功能 : 串口1 DMA 全双工收发
* RxD = P3.6 TxD = P3.7 (P_SW1: S1_S[1:0] = 01)
* 波特率 115200(定时器1模式0, 1T)
* 操作说明:
* 1). 用 STC-ISP/AiCube-ISP 下载,IRC频率选 22.1184MHz
* 2). 打开串口助手,波特率 115200,8-N-1
* 3). 向单片机发送任意数据(少于DMA_AMT_LEN字节)
* 4). 串口1接收中断做超时判断,超时5ms后判定一帧结束
* 5). 主循环中把收到的数据用 DMA 原样发回
************************************************************/
#include "stc8h.h"
#include "intrins.h"
#define MAIN_Fosc 22118400UL // 主频 22.1184MHz
#define Baudrate1 115200UL // 波特率
#define DMA_AMT_LEN 16 // DMA 接收/发送总字节数:实际为 (n+1)=16 字节
// DMA 收发缓冲区(放在 XRAM,DMA 要求地址为扩展 RAM 空间)
unsigned char xdata DMABuffer[DMA_AMT_LEN];
// 全局标志
unsigned char Rx_cnt = 0; // 接收字节计数(用于超时判断)
unsigned char RX1_TimeOut = 0; // 接收超时计数
bit B_1ms = 0; // 1ms 标志
bit DMARxFlag = 0; // DMA 接收完成标志
bit DMATxFlag = 0; // DMA 发送完成标志
bit B_DoSend = 0; // 主循环需要发送的标志
/************************************************************
* 函数名 : Uart1_Init
* 功能 : 串口1初始化, 定时器1做波特率发生器, 定位 RxD=P3.6/TxD=P3.7
************************************************************/
void Uart1_Init(void)
{
// ---- 串口1功能脚切换: P_SW1 的 S1_S[1:0]=01 => P3.6/P3.7 ----
P_SW1 &= 0x3F; // 清零 b7b6, 保留 SPI_S
P_SW1 |= 0x40; // S1_S[1:0]=01 => RxD=P3.6, TxD=P3.7
// ---- P3.6 配置为准双向口(接收), P3.7 配置为推挽输出(发送) ----
P3M1 &= ~0x80; P3M0 |= 0x80; // P3.7: 推挽输出 (M1=0,M0=1)
P3M1 &= ~0x40; P3M0 &= ~0x40; // P3.6: 准双向口 (M1=0,M0=0)
// ---- 波特率: 使用定时器1, 模式0(16位自动重装), 1T模式 ----
TR1 = 0; // 先关闭定时器1
AUXR &= ~0x01; // S1ST2=0: 串口1选择定时器1做波特率发生器
AUXR |= (1 << 6); // T1x12=1: 定时器1为1T模式
TMOD &= ~(1 << 6); // 定时器1设为定时器模式
TMOD &= ~0x30; // 定时器1为16位自动重装载模式
TH1 = (unsigned char)((65536UL - (MAIN_Fosc / 4) / Baudrate1) / 256);
TL1 = (unsigned char)((65536UL - (MAIN_Fosc / 4) / Baudrate1) % 256);
ET1 = 0; // 禁止定时器1中断
TR1 = 1; // 启动定时器1
// ---- 串口1: 模式1(8位数据,可变波特率), 允许接收 ----
SCON = 0x50; // SM0 SM1 = 01, REN = 1
PCON &= 0x7F; // SMOD = 0: 波特率不倍速
ES = 1; // 允许串口1中断(用于超时判断)
}
/************************************************************
* 函数名 : Timer0_Init
* 功能 : 定时器0初始化, 1ms 定时, 用于接收超时判断
************************************************************/
void Timer0_Init(void)
{
AUXR |= (1 << 7); // T0x12=1: 定时器0为1T模式
TMOD &= ~0x03; // 定时器0为16位自动重装载模式
TL0 = (unsigned char)(65536UL - (MAIN_Fosc / 1000)); // 1ms
TH0 = (unsigned char)((65536UL - (MAIN_Fosc / 1000)) / 256);
TF0 = 0; // 清除TF0标志
TR0 = 1; // 启动定时器0
ET0 = 1; // 允许定时器0中断
}
/************************************************************
* 函数名 : DMA_Config
* 功能 : 串口1 DMA 收发配置
************************************************************/
void DMA_Config(void)
{
// ---- UR1T DMA 发送配置 ----
DMA_UR1T_CFG = 0x80; // bit7=1: 使能 UR1T_DMA 中断
DMA_UR1T_STA = 0x00; // 清除发送状态标志
DMA_UR1T_AMT = DMA_AMT_LEN - 1; // 传输总字节数: n+1
DMA_UR1T_TXA = DMABuffer; // 设置发送源地址
DMA_UR1T_CR = 0xC0; // bit7=1:使能 UR1T_DMA; bit6=1:开始自动发送
// ---- UR1R DMA 接收配置 ----
DMA_UR1R_CFG = 0x80; // bit7=1: 使能 UR1R_DMA 中断
DMA_UR1R_STA = 0x00; // 清除接收状态标志
DMA_UR1R_AMT = DMA_AMT_LEN - 1; // 传输总字节数: n+1
DMA_UR1R_RXA = DMABuffer; // 设置接收目标地址
DMA_UR1R_CR = 0xA1; // bit7=1:使能; bit5=1:开始接收; bit0=1:清除FIFO
}
/************************************************************
* 函数名 : UART1_Isr
* 中断号 : 4
* 功能 : 串口1中断, 做接收超时判断
************************************************************/
void UART1_Isr(void) interrupt 4
{
if (RI)
{
RI = 0; // 清除RI位
Rx_cnt++; // 接收计数
if (Rx_cnt >= DMA_AMT_LEN) Rx_cnt = 0;
RX1_TimeOut = 5; // 5ms 没收到新数据, 判定一帧接收完毕
}
if (TI)
{
TI = 0; // 清除TI位
}
}
/************************************************************
* 函数名 : Timer0_Isr
* 中断号 : 1
* 功能 : 定时器0中断, 1ms 标志
************************************************************/
void Timer0_Isr(void) interrupt 1
{
B_1ms = 1; // 1ms 标志
}
/************************************************************
* 函数名 : UART1_DMA_Isr
* 中断号 : 13
* 功能 : 串口1 DMA 中断
************************************************************/
void UART1_DMA_Isr(void) interrupt 13
{
if (DMA_UR1T_STA & 0x01) // 发送完成
{
DMA_UR1T_STA &= ~0x01;
DMATxFlag = 1;
}
if (DMA_UR1T_STA & 0x04) // 数据覆盖
{
DMA_UR1T_STA &= ~0x04;
}
if (DMA_UR1R_STA & 0x01) // 接收完成
{
DMA_UR1R_STA &= ~0x01;
DMARxFlag = 1;
}
if (DMA_UR1R_STA & 0x02) // 数据丢弃
{
DMA_UR1R_STA &= ~0x02;
}
}
/************************************************************
* 函数名 : main
* 功能 : 主函数
************************************************************/
void main(void)
{
unsigned char i;
// ---- 初始化 ----
P_SW2 |= 0x80; // 允许访问 XFR 扩展寄存器
Uart1_Init(); // 串口1初始化
Timer0_Init(); // 定时器0初始化(1ms)
DMA_Config(); // DMA 收发配置
EA = 1; // 打开总中断
while (1)
{
// ---- 1ms 定时处理 ----
if (B_1ms)
{
B_1ms = 0;
if (RX1_TimeOut)
{
RX1_TimeOut--;
if (RX1_TimeOut == 0) // 超时, 一帧接收完毕
{
B_DoSend = 1; // 通知主循环发送
}
}
}
// ---- 接收超时后, 用 DMA 把收到的数据原样发回 ----
if (B_DoSend)
{
B_DoSend = 0;
// 停止并重新配置发送 DMA
DMA_UR1T_CR = 0x00; // 关闭并停止 UR1T_DMA
DMA_UR1T_STA = 0x00; // 清标志
DMA_UR1T_AMT = DMA_AMT_LEN - 1; // 发送字节数
DMA_UR1T_TXA = DMABuffer; // 发送源地址
DMA_UR1T_CR = 0xC0; // 使能 + 触发发送
// 重新启动接收 DMA, 准备下一帧
DMA_UR1R_CR = 0x00; // 关闭并停止 UR1R_DMA
DMA_UR1R_STA = 0x00; // 清标志
DMA_UR1R_AMT = DMA_AMT_LEN - 1;
DMA_UR1R_RXA = DMABuffer;
DMA_UR1R_CR = 0xA1; // 使能 + 开始接收 + 清FIFO
Rx_cnt = 0;
}
}
}
关键点说明
-
引脚切换:P_SW1 的 S1_S[1:0] 位于 B7、B6。P_SW1 &= 0x3F 先清零 b7b6,再 |= 0x40 得到 01,即 RxD=P3.6、TxD=P3.7。这与《功能脚切换》章节的 S1_S[1:0]=01 定义一致。
-
I/O 模式:P3M0/P3M1 中 P3.7 对应 B7、P3.6 对应 B6。
- P3.7 作发送:推挽输出(M1=0, M0=1)
- P3.6 作接收:准双向口(M1=0, M0=0)
-
DMA 总字节数:DMA_UR1T_AMT / DMA_UR1R_AMT 写入的是 n-1,实际传输 n 字节(手册注明"实际字节数为 DMA_AMT+1")。
-
DMA 中断号:串口1的 DMA 中断号为 13(与手册例程一致)。
-
XFR 访问:DMA 相关寄存器(DMA_UR1T_xxx 等)属于扩展寄存器,必须先执行 P_SW2 |= 0x80 打开 EAXFR。
如果您希望改成 接收满固定字节数就回发(不用超时判断),或需要修改波特率,告诉我即可。另外请确认一下您实际使用的波特率,上面的程序默认是 115200,如需 9600 等其它值我可以帮您调整重装值。
🔍 搜索 "STC8H1K17T TSSOP20 串口1 DMA 收发程序"
📌 相关论坛讨论
- STC8H1K17T,串口1定位到P36/P3.7失败!
- 【AI助手】@AiCube-ISP-V6.97中,【自动生成程序】+【查错高手】
- 8H系列,串口1打印问题。
⏱️ 查找用时 8.7s | 总回答用时 20.3s