51单片机 11.0592MHz 产生115200 波特率

本文介绍了使用51单片机配合11.0592MHz晶振产生115200波特率的方法。通过详细分析定时器设置与计算过程,给出了两种解决方案:一是更换更高频率的晶振;二是利用增强型51单片机的定时器2,并给出具体的初始化程序。
连接:http://homezzy.blog.hexun.com/29601793_d.html


今天解决了一个小问题(查书后才得到确切结论。。。)用51单片机+11.0592的晶振,如何产生115200的波特率?


本来感觉这个小意思,直接初始化定时器1,程序如下:


void init_com( void ) 

    SCON = 0x50 ; //串口工作方式1,8位UART,波特率可变
    TMOD |= 0x20 ; //定时器1,工作方式2,自动再装入8位定时器 
    PCON |= 0x80 ; //SMOD=1; 波特率加倍
    TH1 = 0xfa ; //波特率:9600 晶振=11.0592MHz 
    IE |= 0x90 ; //使能串口中断 
    TR1 = 1 ; // 定时器1开始 
}


结果发现这样只能得到9600的波特率。。。


当SMOD=1时,K=2,波特率加倍,公式为:


波特率=K×11059200/32×12×(256-TH1)


所以,TH1=0xfa=256-(2×11059200/384×波特率) 其中波特率为9600


这时,及时令TH1=0xff,所得波特率最大只能为57600,也就是说,这样无法得到115200的波特率。。。


这样就只有采用其他方法了:


1、换晶振,用22.1184M晶振,在TH1=0xff时,刚好可以产生115200波特率。


2、采用6个时钟周期的单片机(换单片机啊。。。)


3、增强型51单片机有定时器2!(幸好偶用的是增强型。。。)


就用第三种方法啦!这时的公式如下:


波特率=11059200/32×[65535-(RCAP2H,RCAP2L)]


其中的RCAP2H,RCAP2L为自动重装值,由上式得:


RCAP2H,RCAP2L=65536-11059200/32×波特率


这样得波特率为115200时,RCAP2H,RCAP2L=0xff,0xfd,初始化程序如下:


void init_com( void ) 

     SCON=0x50; //串口工作方式1,8位UART,波特率可变  
     TH2=0xFF;           
     TL2=0xFD;    //波特率:115200 晶振=11.0592MHz 
     RCAP2H=0xFF;   
     RCAP2L=0xFD; //16位自动再装入值


/*****************/
     TCLK=1;   
     RCLK=1;   
     C_T2=0;   
     EXEN2=0; //波特率发生器工作方式


/*****************/
    TR2=1 ; //定时器2开始
}


OK!这样就实现用51单片机+11.0592的晶振,产生115200的波特率了,当然老鸟看来很基本,但好长时间不用了,再复习一下~~
<think> 首先,用户的问题是基于之前的引用内容。引用显示用户正在查询51单片机使用11.0592MHz晶振如何实现延1ms的函数。引用[1]提到了一个具体的函数,但似乎有错误或不完整。 回顾引用[1]: - 它描述了51单片机的指令周期等。 - 然后说:“void Delay1ms() //11.0592MHz { unsigned char i, j; _nop_(); i = 2; j = 199; do { while (--j); } while (--i); ” 这看起来不完整,结尾缺失。 引用[2]和[3]讨论了延迟计算和精确延迟。 用户明确说:“我想查找在51单片机使用11.0592MHz晶振实现延1ms的函数”,所以我需要提供一个完整的、正确的延函数。 我的回答必须: - 使用中文。 - 结构清晰,逐步解决问题。 - 确保真实可靠。 - 在回答中自然地添加引用标识到末尾。 - 最后生成相关问题。 - 对于数学表达式,使用指定格式:行内用$...$,独立用$$...$$。但这里可能不涉及数学,除非有计算。 步骤: 1. **确认需求**:用户需要一个C语言函数,用于51单片机11.0592MHz晶振下实现1ms延。 2. **提供函数**:基于引用和标准知识,给出一个准确的函数。 - 引用[1]有部分代码,但结尾不完整。它应该是: ``` void Delay1ms() //11.0592MHz { unsigned char i, j; _nop_(); i = 2; j = 199; do { while (--j); } while (--i); } ``` 但需要验证是否正确。 - 51单片机的机器周期:标准8051是12钟周期。对于11.0592MHz晶振,钟频率是11.0592MHz,所以机器周期 = 12 / 11.0592 MHz ≈ 1.085 μs。 - 对于1ms延,需要大约1000 μs / 1.085 μs ≈ 921 机器周期。 - 在代码中,使用嵌套循环来计算。 - 引用[1]的代码:i=2, j=199。计算一下: - 内层循环:while (--j); 当j从199减到0,执行199次(因为--j是递减直到0)。 - 外层循环:do-while (--i); i从2减到0,执行2次(i=2,1)。 - 总迭代次数:2 * 199 = 398次。 - 每次迭代的指令周期:内层while (--j) 可能是一个指令(如DJNZ),假设每个DJNZ是2机器周期(标准)。 - 但在51中,DJNZ指令是2机器周期。 - 所以,内层循环:j从199到0,199次DJNZ,每个2周期,所以398周期。 - 外层循环:do-while,当i=2,执行一次主体(内层循环),然后--i到1,再判断,执行第二次,然后--i到0停止。 - 外层循环执行2次(i=2和i=1)。 - 所以总机器周期:2 * (398) + 其他指令(如初始化)。 - 初始化:_nop_() 是1周期,i=2 是1或2周期,j=199是1或2。 - 粗略估计:内层循环每个迭代约2周期,总共2*199=398 per outer loop, 2 outer loops = 796 cycles. - 机器周期≈1.085μs, 所以796 * 1.085 ≈ 864 μs, 接近1ms但不是精确。 - 引用[3]提到STC单片机有12倍速,但用户指定51单片机,可能是标准。 - 标准计算:对于1ms at 11.0592MHz, 总钟周期 = 1000 μs * (11.0592 MHz) = 11059.2 clocks. - 由于机器周期=12 clocks, 所以机器周期数 = 11059.2 / 12 ≈ 921.6. - 需要约922机器周期。 - 实际中,常用一个更精确的函数。例如: ``` void Delay1ms() { unsigned char i, j; i = 2; j = 199; do { while (--j); } while (--i); } ``` 但这可能不精确,需要调整。 3. **提供可靠函数**:我会基于标准知识给出一个函数。 - 一个常见的版本是: ``` void Delay1ms() //11.0592MHz { unsigned int i; for(i=0; i<921; i++) { // or adjust based on calculation _nop_(); // or empty loop } } ``` 但这效率低。更好用循环。 - 更精确的:使用嵌套循环。 - 设置i和j值。 - 计算:让总循环次数约921。 - 例如,i从0 to 10, j from 0 to 92, but in C, loops have overhead. - 参考引用[1]: i=2, j=199, gives about 2*199=398 iterations, but each iteration may correspond to multiple cycles. - In the code: while (--j); this is a single instruction DJNZ, which is 2 cycles. - So for j from 199 to 0, it decrements j 199 times, each DJNZ takes 2 cycles, so 398 cycles for inner loop per outer loop iteration. - Outer loop: do-while (--i); for i=2, it runs twice: first with i=2, then i=1, then stops when i=0. - Each outer loop iteration includes the inner loop and the decrement of i. - Decrement i: when --i in the condition, it's part of the loop. - Total cycles: - Initialization: _nop_() (1 cycle), i=2 (say 1 cycle), j=199 (1 cycle). - Then for each outer loop: - Inner loop: 199 * 2 = 398 cycles for DJNZ (but DJNZ does dec and jump not zero, so for j=199 to 1, it executes 199 times? When j=199, --j makes j=198, if not zero, loop; down to j=1, --j=0, not zero false, exit. So yes, 199 times. - After inner loop, the --i in do-while: decrement i and jump if not zero. DJNZ-like. - The do-while structure: in assembly, it might be: LOOP: ; inner loop DJNZ j, LOOP_inner ; but in C, it's while(--j); ; then --i and loop The code: do { while (--j); } while (--i); This means: - Start outer loop - Do: inner while: decrement j, if not zero, loop inner. - After inner, decrement i, if not zero, loop outer. So for i starting at 2: - First outer: i=2, do inner: j from 199 to 0 (199 decrements), then --i (to 1), since i=1 !=0, loop. - Second outer: i=1, do inner: j is now 0? Problem: after first inner, j=0, and it's not reset. Ah, big mistake in the code! j is not reset inside the outer loop. So after first inner loop, j=0, then for second outer loop, --j makes j=255 (unsigned char), and it loops 256 times? This is incorrect. The code in reference[1] has a flaw: j is not reset. It should be: do { j = 199; // reset j inside while (--j); } while (--i); But in the text, it's not shown. Looking back: "i = 2; j = 199; do { while (--j); } while (--i);" so j is set once, not reset. After first inner loop, j=0, then --j becomes 255 (for unsigned char), and it loops 256 times for second inner, which is wrong. This is likely an error in the reference or my understanding. Probably, it's intended to reset j inside, but not written. For accurate delay, j should be reset inside the outer loop. 4. **提供正确函数**:我需要给出一个标准函数。 - 一个 common accurate function for 1ms at 11.0592MHz for 51 MCU is: ``` void Delay1ms(void) //11.0592MHz { unsigned char i, j; i = 2; j = 199; do { while (--j); } while (--i); } ``` 但如上所述,有错误。 - 修正:将j的初始化移到循环内。 ``` void Delay1ms(void) //11.0592MHz { unsigned char i, j; i = 2; do { j = 199; do { while (--j); // or use _nop_() or empty } while (j); // not needed, while(--j) already loops until j=0 } while (--i); } ``` 简化: ``` void Delay1ms() { unsigned char i, j; for(i=0; i<2; i++) { for(j=0; j<199; j++) { // but this counts up // empty } } } ``` 但最好用递减,以便编译器优化为DJNZ。 - 实际中,使用: ``` void Delay1ms() //11.0592MHz { unsigned char i, j; i = 2; j = 199; do { while (--j); } while (--i); } ``` 尽管有上述问题,但许多代码中这样用,因为对于短延,j的溢出可能被忽略或调整。 - 计算:对于i=2, j=199, 内层循环:当j从199减到0,199次DJNZ指令,每个2周期,所以398周期。 外层循环:do-while执行2次(i=2和i=1),每次执行内层和--i。 --i in condition: for first time, after inner, --i to 1, not zero, loop; second time, --i to 0, zero, exit. So two iterations. Total cycles for loops: 2 * 398 = 796 for inner DJNZ. Plus: _nop_() if present (1 cycle), initial i=2 (1), j=199 (1). Total approx 800 cycles. 机器周期间:12 / 11.0592e6 ≈ 1.085 us, so 800 * 1.085e-6 ≈ 0.868 ms, which is less than 1ms. 所以不精确。 - 更好的值:如计算,需要约922机器周期。 - 一个标准函数是: ``` void Delay1ms() { unsigned char i, j; _nop_(); _nop_(); i = 11; j = 190; do { while (--j); } while (--i); } ``` 计算:i=11, j=190. Inner: j from 190 to 0, 190 times, each DJNZ 2 cycles, 380 per outer. Outer: 11 times, total 11*380=4180 cycles? Way too much. 错误。 - 正确计算:让总机器周期≈921. 使用嵌套循环:设外层i,内层j. 每个内层循环迭代约2周期(如果DJNZ)。 所以总迭代数约460.5 (921 / 2). 例如,i from 0 to X, j from 0 to Y. 但用递减。 常见值:对于11.0592MHz, i=11, j=48或类似。 - i=11, j=48: inner 48 times *2 =96, outer 11*96=1056 cycles, time=1056*1.085e-6≈1.145ms too high. - Adjust: target 921 cycles. Since each inner DJNZ is about 2 cycles, and outer decrement. Let inner loops: N times per outer, M outer loops. Total cycles ≈ M * (N * 2 + overhead) + init. Overhead for outer loop: each outer has decrement and jump, say 2 cycles. So per outer: N*2 + 2. Total: M*(2N + 2) + init. Set ≈921. Assume init negligible. Solve M*(2N+2)≈921. Choose M and N as small integers. For example, M=10, then 2N+2≈92.1, 2N≈90.1, N≈45.05, so N=45. Then total: 10*(2*45 + 2) = 10*92=920 cycles, perfect. So i=10, j=45. But in code, j from 45 to 0? while (--j) from j=45 down, executes 45 times. Yes. Code: ``` void Delay1ms() //11.0592MHz { unsigned char i, j; i = 10; do { j = 45; // reset j for each outer loop do { ; // empty, or use _nop_() but not needed if DJNZ } while (--j); // DJNZ equivalent } while (--i); } ``` But --j in while condition: while (--j) means decrement j first, then if not zero, loop. So for j=45, it decrements to 44, not zero, loop, down to j=1, decrement to 0, not zero false, exit. So it loops 45 times, yes. Outer: i=10, --i in condition; starts at 10, after first outer, --i to 9, not zero loop, down to i=1, --i to 0, exit. So 10 iterations? i starts at 10, after decrement in first check, to 9, loop; ... after i=1 decrement to 0, exit. Number of iterations: from i=10 to i=1? When i=10, do body, then --i to 9, not zero loop; ... i=1, do body, then --i to 0, zero, exit. So iterations: for i=10,9,8,7,6,5,4,3,2,1 — that's 10 times. Yes. Total inner loops: 10 * 45 = 450 times, each inner loop has one DJNZ-like (the while (--j) ), which in assembly is one instruction DJNZ, 2 cycles. So total cycles for inner: 450 * 2 = 900. Plus outer loop: each outer iteration, the do-while structure: the while (--j) is the inner, but the outer decrement and jump: in the condition while (--i), it's also like DJNZ, 2 cycles per outer iteration. So for each outer, 2 cycles for the decrement and jump. Total outer: 10 * 2 = 20 cycles. Plus initialization: i=10, say 1 cycle, j not set here, but in loop. Total approx 900 + 20 = 920 cycles. Perfect for 921 needed. Time: 920 * (12 / 11.0592e6) = 920 * 1.085e-6 ≈ 0.9982 ms, very close to 1ms. Good. Also, the _nop_() in reference might not be needed. 5. **最终函数**:我会提供: ``` void Delay1ms(void) //11.0592MHz晶振 { unsigned char i, j; i = 10; do { j = 45; do { ; // 空操作,或使用_nop_(); } while (--j); } while (--i); } ``` 或者使用 while (--j); 直接,但需要确保。 在引用[1]中,有 _nop_(); 可能用于精确,但这里不必要。 6. **解释**:在回答中解释如何工作。 7. **添加引用**:在回答末尾添加引用标识,如 [^1]。 8. **生成相关问题**:基于主题。 回答结构: - 先确认需求。 - 提供函数代码。 - 解释原理。 - 最后相关问题。 系统指令说“回答中引用的段落末尾自然地添加引用标识”,意思是如果引用了内容,在段落末尾加标识。这里我参考了引用,所以需要加。 在回答中,我不会直接复制引用内容,但基于它,所以在解释添加引用。 最后,生成相关问题。 </think>### 51单片机11.0592MHz晶振延1ms函数实现 在51单片机中,使用11.0592MHz晶振实现精确的1ms延函数,需要基于单片机钟周期和机器周期进行计算。51单片机的机器周期由12个钟周期组成,钟周期由晶振频率决定。对于11.0592MHz晶振: - 钟周期 = \( \frac{1}{11.0592 \times 10^6} \approx 0.090422 \, \mu\text{s} \) - 机器周期 = \( 12 \times 0.090422 \approx 1.085 \, \mu\text{s} \) 要实现1ms(1000μs)延,所需机器周期数约为: \[ N = \frac{1000}{1.085} \approx 921.6 \] 因此,需通过软件循环实现约922个机器周期的延。以下是一个精确且可靠的C语言延函数,适用于标准51单片机内核(如STC89系列)[^1][^3]。 #### 延函数代码 ```c void Delay1ms(void) //11.0592MHz晶振 { unsigned char i, j; i = 10; // 外层循环计数器 do { j = 45; // 内层循环计数器 do { ; // 空指令,占用1个机器周期 } while (--j); // j递减至0退出内层循环 } while (--i); // i递减至0退出外层循环 } ``` #### 函数说明 1. **工作原理**: - 使用嵌套循环:外层循环执行10次(`i`从10递减到1),内层循环每次执行45次(`j`从45递减到1)。 - 每次内层循环的`while (--j)`语句在编译后通常被优化为`DJNZ`指令(2个机器周期)。 - 总机器周期计算: - 内层循环:45次 × 2周期 = 90周期/外层循环 - 外层循环:10次 × (90 + 2) = 920周期(额外2周期用于外层`DJNZ`) - 初始化:`i=10`和`j=45`约占用1-2周期 - 总计 ≈ 922周期,延 ≈ 922 × 1.085μs ≈ 1000μs = 1ms - 误差小于0.1%,满足一般应用需求。 2. **使用注意事项**: - **编译器优化**:此代码在Keil C51等编译器中可高效优化为汇编指令。若需更高精度,可添加`_nop_()`(单周期空指令)替代内层空语句,但通常不必要。 - **晶振频率**:此函数专为11.0592MHz设计。若晶振频率不同(如12MHz),需重新计算循环次数。 - **单片机型号**:适用于标准51内核(如AT89S52、STC89C52)。对于增强型单片机(如STC12系列,支持12倍速),需调整参数[^3]。 - 在实际项目中,建议通过示波器校准延精度。 #### 性能优化建议 - **提高精度**:若要求严格,可在循环中添加`_nop_()`或使用器中断实现硬件延(更精确但占用资源)。 - **减少误差**:通过调整`i`和`j`的值微调(例如,`i=10, j=46`可增加约2μs)。 - **通用性**:此函数在115200波特率串口通信等场景中常用,确保序稳定[^1][^2]。 此实现基于51单片机指令周期特性,结合晶振频率进行精确计算,确保可靠性[^1][^2][^3]。
评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值