基于STC12C5A60S2系列1T 8051单片机的液晶显示器LCD1602显示整数、小数应用

STC12C5A60S2系列1T 8051单片机管脚图

在这里插入图片描述在这里插入图片描述

STC12C5A60S2系列1T 8051单片机I/O口各种不同工作模式及配置

在这里插入图片描述

STC12C5A60S2系列1T 8051单片机I/O口各种不同工作模式介绍

在这里插入图片描述在这里插入图片描述

液晶显示器LCD1602简单介绍

在这里插入图片描述在这里插入图片描述
DDRAM : 数据显示随机存储器 液晶显示器显示数据是来于数据显示随机存储器
在这里插入图片描述在这里插入图片描述在这里插入图片描述在这里插入图片描述
CGRAM:字符生成随机存储器 用户自定义字符会存储在字符生成存储器
在这里插入图片描述
CGROM:字符库只读存储器 只提供字符 用液晶显示器显示这些字符

总结:不管是CGRAM(字符生成随机存储器)自定义的字符 还是CGROM(字符库只读存储器)原有的字符 写进DDRAM(数据显示随机存储器)内存地址上 才能在液晶显示器屏幕显示出来
在这里插入图片描述在这里插入图片描述在这里插入图片描述在这里插入图片描述在这里插入图片描述在这里插入图片描述

IIC通信简单介绍

在这里插入图片描述在这里插入图片描述在这里插入图片描述在这里插入图片描述在这里插入图片描述在这里插入图片描述在这里插入图片描述在这里插入图片描述在这里插入图片描述在这里插入图片描述

掉电保存数据芯片24C02简单介绍

在这里插入图片描述在这里插入图片描述在这里插入图片描述在这里插入图片描述在这里插入图片描述在这里插入图片描述在这里插入图片描述在这里插入图片描述在这里插入图片描述在这里插入图片描述在这里插入图片描述在这里插入图片描述在这里插入图片描述在这里插入图片描述在这里插入图片描述在这里插入图片描述在这里插入图片描述在这里插入图片描述在这里插入图片描述在这里插入图片描述在这里插入图片描述在这里插入图片描述

通过液晶显示器LCD1602显示整数、小数

在这里插入图片描述在这里插入图片描述在这里插入图片描述在这里插入图片描述在这里插入图片描述在这里插入图片描述
main.c

#include "STC12C5A60S2.h" 
//#include "AT24C02.h" 
//#include "AT24C02OfI2C.h" 
//#include <intrins.h>
#include <stdio.h>
#include "LCD1602.h" 
#define uchar unsigned char//自定义无符号字符型为uchar
#define uint unsigned int//自定义无符号整数型为uint 
 void main()//主函数
{
   
   
//  uchar Data;//声明数据变量
//	uchar String[10];//声明字符串数组变量
//	LCD1602Init();//液晶显示器初始化函数
//	uchar i;//声明循环变量
//  uchar String[20];//声明字符串数组变量
//  uchar StringCache[5];//声明字符串缓存数组变量
//  LCD1602Init();//液晶显示器初始化函数
//  Data = ReadByteFromAT24C02(0x50,0x20);/
<think>我们使用STC12C5A60S2单片机,要求: 1. K1按键(假设接在某个IO口,比如P1.0)控制P3.2输出方波,频率范围300Hz至2400Hz,每次按键切换频率(例如按一次300Hz,再按一次600Hz,...直到2400Hz再回到300Hz,循环)。 2. 同时,P2.4输出10个脉冲周期(即10个方波)后停止,每次按键切换频率时,P2.4重新输出10个脉冲。 3. 通过P0口连接74HC245驱动数码管显示当前频率序号(1-8,对应8个频率,比如1:300Hz, 2:600Hz, ..., 8:2400Hz)。 分析: 频率范围:300Hz, 600Hz, 900Hz, 1200Hz, 1500Hz, 1800Hz, 2100Hz, 2400Hz。共8个频率点,每次按键切换一个频率点,循环。 实现思路: 1. 使用定时器中断产生方波。由于有两个引脚需要输出方波(P3.2和P2.4),且P2.4每次只输出10个脉冲,我们可以考虑使用同一个定时器,但通过软件计数来产生不同频率和脉冲个数。 2. 我们选择定时器0作为方波生成的定时器。因为STC12C5A60S2有两个定时器(定时器0和定时器1),我们可以用定时器0来产生方波,定时器1用于数码管扫描(如果需要动态扫描)或者用延时函数实现数码管显示(静态显示也可以,但一般为了节省IO口,采用动态扫描)。 3. 注意:P3.2是外部中断0的引脚,但这里我们用作普通IO输出方波。 4. 数码管显示:我们使用P0口连接74HC245(驱动数码管的段选),另外还需要位选信号(这里假设使用P2.0-P2.3作为位选,但题目没有给定位选引脚,我们假设使用P2口的部分引脚作为位选)。由于题目要求显示1-8,我们只需要一个数码管即可(或者用多个数码管中的一位),这里我们简化使用一个数码管显示数字(1-8)。 具体步骤: 1. 初始化定时器0,设置初始中断时间(比如300Hz对应的周期为1/300秒,半周期翻转一次,所以定时时间为1/(2*300)秒)。 2. 在定时器0中断服务程序中: - 对P3.2进行翻转(产生方波)。 - 同时,对P2.4的输出进行控制:在需要输出脉冲时,也翻转P2.4,并且计数(每输出一个完整的方波,需要两次翻转,所以一个周期计数两次?)。我们可以在每次翻转时计数,当计数达到20(10个周期,因为每个周期翻转两次)时,停止P2.4的输出(将其置低或高,保持不再翻转)并清零计数。 3. 按键检测:检测K1按键(假设接在P1.0),当检测到按键按下时,切换到下一个频率点(共8个),同时重新启动P2.4输出(即允许定时器中断中再次对P2.4进行翻转,并重新计数)。 4. 数码管显示显示当前频率序号(1-8)。这里我们采用静态显示,因为只显示一个数字,但实际中可能需要动态扫描(如果使用多位数码管,我们只显示其中一位)。为了简化,我们假设使用一个共阴数码管,将P0口连接到74HC245的输入,74HC245的输出连接数码管的段选。另外,位选我们直接选通一位(例如,如果使用4位数码管,我们只让其中一位的位选有效,其他位选无效)。这里我们假设位选使用P2.5(任意一个未使用的IO)控制,或者使用P2口其他引脚(注意P2.4已经被使用,所以避开)。 但注意:题目要求P0.0-P0.7连接74HC245驱动数码管。74HC245是双向驱动器,我们这里用作输出,所以DIR方向控制要接高电平或低电平(根据数据手册,我们假设DIR接高电平,即A端到B端,A端接P0,B端接数码管段选)。位选信号我们使用P2.1(假设)控制数码管的共阴极(或共阳极,这里假设共阴,那么位选为高电平有效?不对,共阴数码管位选是低电平有效?具体看电路连接)。这里我们假设位选使用P2.1,并且数码管为共阴,那么要显示的那一位给低电平,其他给高电平(如果有多位的话)。由于我们只显示一位,所以只需要一个位选信号。 然而,题目要求显示1-8,我们用一个数码管即可,所以位选可以固定(只接一个数码管,那么位选一直有效)。但为了防止干扰,我们最好还是控制位选。 由于题目没有给出具体的数码管连接电路,我们简化处理: - 使用一个数码管(共阴),其段选通过74HC245连接到P0口,位选接在P2.1(假设),且需要将位选置为有效(低电平有效,所以P2.1=0)。 - 注意:P2.4已经被使用(输出脉冲),所以位选不能使用P2.4,我们使用P2.1(或其他未使用的引脚)。 5. 频率点的存储:我们可以用一个数组存储8个频率对应的定时器重装值。由于方波的周期T=1/f,而定时器中断是半周期翻转一次,所以定时器中断周期为T/2=1/(2f)。定时器采用16位自动重装模式,那么重装值 = 65536 - (T/2)* (晶振频率/12) (因为STC12C5A60S21T单片机,但默认是12T,具体看设置。STC12C5A60S2可以设置1T或12T,我们这里假设使用12T模式,晶振频率为11.0592MHz,这样定时准确)。 计算定时器重装值: 定时器计数周期 = 1 / (Fosc/12) = 12 / Fosc 秒。 所以,定时时间t对应的计数值N = t / (12/Fosc) = t * Fosc / 12。 对于半周期时间t_half = 1/(2*f) 秒,则: N = t_half * Fosc / 12 = (1/(2*f)) * Fosc / 12 = Fosc/(24*f) 重装值 = 65536 - N 例如,f=300Hz, Fosc=11059200Hz N = 11059200 / (24*300) = 11059200 / 7200 = 1536 重装值 = 65536 - 1536 = 64000 = 0xFA00 但是,由于1536是整数,所以这样计算是准确的。但其他频率呢? 我们计算一下600Hz:N=11059200/(24*600)=11059200/14400=768,重装值=65536-768=64768=0xFD00 900Hz: N=11059200/(24*900)=11059200/21600=512,重装值=65536-512=65024=0xFE00 1200Hz: N=11059200/(24*1200)=11059200/28800=384,重装值=65536-384=65152=0xFE80 注意:384*30=11520,而11059200/28800=384,所以是整数1500Hz: N=11059200/(24*1500)=11059200/36000=307.2,不是整数!这样就会产生误差。 因此,我们需要重新考虑定时器设置。由于STC12C5A60S2的定时器可以工作在1T模式(速度是传统5112倍),这样定时器计数频率就是Fosc,而不是Fosc/12。我们使用1T模式,可以减小误差。 在1T模式下: 计数周期 = 1/Fosc 半周期t_half = 1/(2*f) 计数值N = t_half / (1/Fosc) = Fosc/(2*f) 重装值 = 65536 - N 使用1T模式,重新计算: f=300: N = 11059200/(2*300)=11059200/600=18432,重装值=65536-18432=47104=0xB800 f=600: N=11059200/(2*600)=9216,重装值=65536-9216=56320=0xDC00 f=900: N=11059200/(2*900)=6144,重装值=65536-6144=59392=0xE800 f=1200: N=11059200/(2*1200)=4616,重装值=65536-4616=60920=0xEDF8 -> 注意,4616不是整数11059200/2400=4616.333...,所以有小数,我们取整,这样会有误差。 为了减小误差,我们可以使用定时器的自动重装模式,并允许在中断中修改重装值。对于非整数的情况,我们可以采用在中断中动态调整重装值(比如,有时候用N,有时候用N+1)来平均,但这样复杂。 或者,我们选择晶振频率为12MHz,这样计算: Fosc=12000000 1T模式:N = Fosc/(2*f) = 12000000/(2*f)=6000000/f 300Hz: N=6000000/300=20000,重装值=65536-20000=45536=0xB1E0 600Hz: N=6000000/600=10000,重装值=55536=0xD8F0 900Hz: N=6000000/900≈6666.666,取6667,重装值=65536-6667=58869=0xE5F5 1200Hz: N=6000000/1200=5000,重装值=60536=0xEC78 1500Hz: N=6000000/1500=4000,重装值=61536=0xF060 1800Hz: N=6000000/1800≈3333.333,取3333,重装值=65536-3333=62203=0xF2FB 2100Hz: N=6000000/2100≈2857.142,取2857,重装值=65536-2857=62679=0xF4D7 2400Hz: N=6000000/2400=2500,重装值=63036=0xF63C 这样,除了非整数的情况,我们取整,会有一定误差,但可以接受。 考虑到实际应用,我们使用12MHz晶振,1T模式。 6. 程序结构: - 初始化:设置定时器0为16位自动重装模式,1T模式,设置初始重装值(对应300Hz),打开中断,启动定时器。 - 设置P3.2和P2.4为推挽输出(因为STC12C5A60S2的IO口可以设置模式,这里设置为推挽输出)。 - 设置按键(P1.0)为输入模式(准双向即可)。 - 数码管显示初始化:设置P0口和位选引脚(P2.1)为推挽输出,并显示初始序号1。 - 在定时器0中断服务程序中: 翻转P3.2(输出方波)。 如果当前需要输出P2.4脉冲(即计数使能),则翻转P2.4,并增加翻转次数计数(每翻转一次,计数一次,注意一个周期翻转两次)。当翻转次数达到20(10个周期)时,停止翻转(将P2.4保持为低电平或高电平?我们可以在停止后将其置为低电平,并禁止再翻转(直到下一次按键))。 - 主循环中检测按键,按键去抖,切换频率,并重置P2.4的输出使能和计数,同时更新数码管显示。 7. 变量定义: - 当前频率序号(1-8):index - 频率对应的重装值数组:timer0_reload[8] = {0xB1E0, 0xD8F0, 0xE5F5, 0xEC78, 0xF060, 0xF2FB, 0xF4D7, 0xF63C} - P2.4输出控制:flag_pulse_enable(允许输出脉冲)和pulse_count(当前已经翻转的次数) 8. 注意:在切换频率时,要重新设置定时器0的重装值(在中断中修改也可以,但我们在主循环中修改,因为重装值在中断中会重新加载,我们在主循环中修改重装值后,下次中断就会使用新的重装值)。 9. 数码管显示:用一个数组存储0-9的段码(共阴数码管),例如: unsigned char code seg_table[] = {0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f}; //0-9 显示数字n时,P0=seg_table[n]; 同时位选有效(我们假设只有一位数码管,位选一直有效,或者用P2.1控制位选,我们将其置0(共阴))。 10. 按键处理:去抖,等待按键释放。 代码框架: 注意:STC12C5A60S2的特殊功能寄存器设置: AUXR寄存器:设置T0x12(第7位)为1,定时器0为1T模式。 TMOD:设置定时器0为16位自动重装模式(实际上自动重装模式是TMOD=0x00,但通常使用模式116位不自动重装)?不对,自动重装模式是模式2(8位自动重装),但16位自动重装需要设置TMOD=0x00,然后使用T0的工作方式0(16位自动重装)?实际上,STC12C5A60S2的定时器0有4种模式,模式0(16位自动重装)是通过AUXR中的T0_CT(AUXR.6)和T0_GATE(AUXR.5)来设置的。我们查阅手册:STC12C5A60S2的定时器0有4种模式: 模式0:16位自动重装 模式116位不自动重装 模式2:8位自动重装 模式3:不可屏蔽中断的16位自动重装 我们使用模式0(16位自动重装):设置AUXR中的T0_CT=0(定时器模式),T0_GATE=0(仅由TR0启动),TMOD的低4位为0000(即TMOD&=0xF0; TMOD|=0x00;),然后设置AUXR中的T0_M0和T0_M1(在AUXR中,T0_M0是AUXR.6,T0_M1是AUXR.7?不对,手册上写:T0_CT是AUXR.6,T0_GATE是AUXR.5,而T0_M0和T0_M1在AUXR.4和AUXR.3?没有找到明确的说明,实际上STC12C5A60S2的定时器模式设置比较特殊,建议参考手册。 根据手册,设置定时器0为16位自动重装模式: TMOD &= 0xF0; // 清除T0的控制位 TMOD |= 0x00; // 模式0(16位自动重装)?实际上,在STC12C5A60S2中,定时器0的模式0是13位定时器,模式116位定时器(不自动重装),模式2是8位自动重装,模式3是16位自动重装(不可屏蔽中断)?不对,在数据手册中,定时器0的模式由TMOD的M1和M0位设置: M1=0, M0=0: 模式0,13位定时器 M1=0, M0=1: 模式116位定时器(不自动重装) M1=1, M0=0: 模式2,8位自动重装 M1=1, M0=1: 模式3,两个8位定时器(只适用于T0) 我们想要16位自动重装,但标准51没有,STC12C5A60S2增加了模式0(16位自动重装)吗?实际上,STC12C5A60S2的定时器0可以设置为16位自动重装模式,但需要设置AUXR寄存器中的T0_CT和T0_GATE,以及TMOD中的M1和M0。具体如下: 设置16位自动重装模式:设置TMOD的M1=0, M0=0(即模式0)?不对,模式0是13位。所以STC12C5A60S2的定时器0没有16位自动重装模式?那我们可以使用模式2(8位自动重装)来模拟,但这样精度不够(最大255)。或者使用模式116位不自动重装),然后在中断中重装。为了精确,我们使用模式116位不自动重装),在中断中重装。 因此,我们使用定时器0模式116位不自动重装),在中断中重装。 步骤: 设置TMOD: TMOD &= 0xF0; TMOD |= 0x01; // 模式116位定时器 设置AUXR: AUXR |= 0x80; // 设置T0为1T模式(如果使用1T模式,AUXR的T0x12位(第7位)为1) 定时器重装值:TH0 = (65536 - N) >> 8; TL0 = (65536 - N) & 0xFF; 然后启动定时器:TR0=1; ET0=1; EA=1; 在中断服务程序中,需要重新装入初值(因为模式1不会自动重装)。 由于频率变化时重装值不同,我们在中断中根据当前频率的重装值来重装。 代码示例: 注意:由于中断频率较高,中断服务程序要尽量短。 我们使用全局变量来存储当前频率的重装值(timer0_reload_value),以及P2.4脉冲计数。 变量定义: unsigned int timer0_reload[8] = {0xB1E0, 0xD8F0, 0xE5F5, 0xEC78, 0xF060, 0xF2FB, 0xF4D7, 0xF63C}; // 12MHz晶振,1T模式下的重装值(16位) unsigned char index = 0; // 当前频率序号0-7(对应1-8) bit pulse_enable = 0; // 是否允许P2.4输出脉冲(0:停止,1:允许) unsigned char pulse_count = 0; // 当前已经翻转的次数(每翻转一次加110个周期需要20次翻转?不对,一个周期有两次翻转(从低到高,再从高到低),所以10个周期需要20次翻转?但我们计数的是翻转次数,每翻转一次,输出半个周期。所以10个周期需要20次翻转?是的。但注意,我们每次翻转都计数,当计数到20时,停止。 但是,我们也可以这样:每输出一个完整的周期(两次翻转)计一次数,这样计数10次即可。这样在中断中,每次翻转P2.4后,如果是下降沿(或上升沿)则计数?这样复杂。我们直接按翻转次数计数。 定时器0中断服务程序: void timer0_isr() interrupt 1 { TH0 = (unsigned char)(timer0_reload[index] >> 8); TL0 = (unsigned char)(timer0_reload[index] & 0xFF); // 翻转P3.2 P32 = ~P32; // 假设P3.2定义为sbit P32=P3^2; // 处理P2.4 if (pulse_enable) { P24 = ~P24; // 翻转P2.4 pulse_count++; if (pulse_count >= 20) // 10个周期(20次翻转) { pulse_enable = 0; // 停止翻转 P24 = 0; // 停止后置为低电平(也可以置为高,根据需求) } } } 按键处理(在主循环): if (K1按下) // 假设K1接在P1.0 { 延时去抖; if (K1确实按下) { index = (index+1) % 8; // 切换到下一个频率 // 更新数码管显示显示index+1 display_number(index+1); // 重新启动P2.4脉冲输出 pulse_enable = 1; pulse_count = 0; // 等待按键释放 while(!K1); // 等待释放,这里可以加延时去抖 } } 数码管显示函数: void display_number(unsigned char num) { // 段码表,共阴数码管,0-9 unsigned char code seg_table[] = {0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f}; // 显示数字num(1-8),所以num-1对应0-7,但我们的表是0-9,所以直接取seg_table[num]即可?不对,num是1-8,所以seg_table[num]是1的段码?不对,1的段码是seg_table[1]。 // 所以,显示num,就是seg_table[num] // 但是注意,我们只显示一位,所以不需要位选切换。这里我们假设数码管位选已经设置好(一直有效),或者每次显示都设置位选有效。 P0 = seg_table[num]; // 注意:这里P0输出的是段码,但74HC245是驱动,所以直接给P0赋值即可。 // 同时,确保位选有效:假设位选接P2.1(低电平有效) P2_1 = 0; // 位选有效(共阴,低电平点亮) } 但是,注意P2口除了P2.1(位选)和P2.4(脉冲输出)外,其他位不要影响。所以,我们设置位选时,不能影响P2.4。我们可以用位操作: P2 = (P2 & 0xFD) | 0x00; // 将P2.1置0,其他位不变。但注意,这样会同时改变P2.4?不会,因为P2.4是输出脉冲,我们在中断中会改变它,但主循环中改变P2.1不会影响P2.4,因为P2.4在中断中翻转。 但是,在中断中翻转P2.4时,可能会和主循环中操作P2口冲突。为了避免冲突,我们使用sbit来单独控制P2.4和位选(P2.1)。这样,操作sbit不会影响其他位。 11. 初始化: void init() { // 设置定时器0 TMOD &= 0xF0; // 清除T0的设置 TMOD |= 0x01; // 定时器0,模式116位不自动重装) AUXR |= 0x80; // T0为1T模式(1T:1个时钟周期计数一次,12T:12个时钟周期计数一次) TH0 = (unsigned char)(timer0_reload[index] >> 8); TL0 = (unsigned char)(timer0_reload[index] & 0xFF); ET0 = 1; // 允许定时器0中断 TR0 = 1; // 启动定时器0 EA = 1; // 总中断 // 设置IO口 P3M0 = 0x04; // 设置P3.2为推挽输出(因为要输出方波,需要强推挽) P3M1 = 0x00; P2M0 = 0x10; // 设置P2.4为推挽输出 P2M1 = 0x00; // P0口用于数码管段选,设置为推挽输出 P0M0 = 0xFF; P0M1 = 0x00; // P1.0按键设置为准双向(输入) P1M0 = 0x00; P1M1 = 0x00; // 准双向 // 初始化变量 index = 0; pulse_enable = 1; // 一开始就允许输出10个脉冲 pulse_count = 0; // 初始显示 display_number(index+1); // 显示1 } 12. 主函数: void main() { init(); while(1) { if (K1 == 0) // 假设按键按下为低电平 { delay_ms(10); // 去抖 if (K1 == 0) { index = (index+1) % 8; display_number(index+1); pulse_enable = 1; pulse_count = 0; // 等待按键释放 while(K1==0); // 等待释放 delay_ms(10); } } } } 13. 注意:中断服务程序中的重装值更新:由于index是全局变量,在中断中读取,主循环中修改,所以需要防止中断中读取到不完整的值(但index是8位,在51上是原子操作,所以安全)。但是,当index在主循环中修改时,中断可能发生,此时中断中就会使用新的重装值。这样是合理的。 14. 关于P2.4的输出:在中断中,当pulse_enable为1时,每次翻转P2.4,并计数。当计数达到20时,停止并清零pulse_count?实际上,我们只将pulse_enable置0,pulse_count保留为20。下一次按键时,pulse_count会被重置为0。这样也可以。 15. 但是,注意:在切换频率时,我们重新启动P2.4输出(pulse_enable=1, pulse_count=0),这样P2.4就会重新输出10个周期的脉冲。 16. 关于数码管:我们使用静态显示,所以显示一次后,只要不改变,数码管就一直显示那个数字。所以主循环中不需要持续刷新显示17. 另外,注意74HC245的方向控制:我们假设DIR接高电平(A->B),A接P0,B接数码管段选。所以DIR引脚可以接VCC。 18. 位选:我们假设使用P2.1控制一个数码管的位选(共阴数码管,低电平有效),所以初始化后,将P2.1置0,然后显示数字时,只需要改变P0口即可。 19. 但是,在初始化时,我们设置了P0为推挽输出,P2.1也要设置为推挽输出(低电平驱动数码管)。在初始化中,我们设置了P2M0和P2M1,其中P2.4设置为推挽,P2.1我们还没有设置。所以需要设置P2.1为推挽: P2M0 |= 0x02; // 0000 0010,设置P2.1为推挽 P2M1 &= ~0x02; 20. 最后,注意:在显示函数中,我们只设置了P0和P2.1,所以其他位选(如果有多个数码管)应该关闭(即置高电平,不选通)。因为我们只用一个数码管,所以其他位选(如果有)可以不管,但为了安全,最好将其他位选关闭。 由于我们只用一个数码管,所以没有问题。 21. 关于定时器中断频率:最高频率2400Hz,半周期为1/4800秒,中断周期为208.3us。在12MHz晶振1T模式下,一个机器周期1us,所以中断服务程序必须在208us内执行完毕。我们的中断服务程序很短,所以没问题。 22. 但是,在中断中重装定时器值,会引入一定的误差(因为中断响应和重装需要时间)。我们可以通过调整重装值来补偿。但这里为了简单,忽略不计。 23. 如果使用标准12T模式,中断频率高时可能来不及响应,所以使用1T模式。 总结:我们使用12MHz晶振,1T模式,定时器0模式116位不自动重装),在中断中重装,并翻转P3.2和P2.4(当使能时)。按键切换频率,并重新启动P2.4的10个脉冲输出。数码管显示当前频率序号。 由于计算重装值时,部分频率不是整数,所以会有轻微误差。如果需要更精确,可以使用更高频率的晶振,或者使用定时器的其他模式(如PWM模式)来输出方波,但题目要求使用普通IO。 下面给出完整代码:</think>### 实现方案:STC12C5A60S2单片机按键控制方波输出与数码管显示 #### 1. 硬件连接设计 - **单片机**:STC12C5A60S21T模式,12MHz晶振) - **按键K1**:接P1.0(下降沿触发) - **方波输出**: - P3.2输出主方波(频率300-2400Hz) - P2.4输出10周期脉冲 - **数码管驱动**: - P0口 → 74HC245(A端)→ 数码管段选(共阴) - P2.1控制数码管位选(低电平有效) - **74HC245方向控制**:DIR接VCC(A→B方向) #### 2. 频率计算与定时器配置 使用1T模式(12MHz晶振),定时器0模式116位定时),频率对应的重装值: $$ \text{重装值} = 65536 - \frac{12000000}{2 \times f} $$ | 序号 | 频率 (Hz) | 重装值(十六进制) | |------|-----------|-------------------| | 1 | 300 | 0xB1E0 | | 2 | 600 | 0xD8F0 | | 3 | 900 | 0xE5F5 | | 4 | 1200 | 0xEC78 | | 5 | 1500 | 0xF060 | | 6 | 1800 | 0xF2FB | | 7 | 2100 | 0xF4D7 | | 8 | 2400 | 0xF63C | #### 3. 程序实现 ```c #include <STC12C5A60S2.H> // 频率重装值表 (12MHz, 1T模式) unsigned int code timer0_reload[8] = { 0xB1E0, 0xD8F0, 0xE5F5, 0xEC78, 0xF060, 0xF2FB, 0xF4D7, 0xF63C }; unsigned char index = 0; // 当前频率序号(0-7) bit pulse_enable = 1; // P2.4脉冲输出使能 unsigned char pulse_count = 0; // 脉冲翻转计数 // 数码管段码表 (共阴, 0-9) unsigned char code seg_table[] = { 0x3F, 0x06, 0x5B, 0x4F, 0x66, 0x6D, 0x7D, 0x07, 0x7F, 0x6F }; // 数码管显示函数 void display_number(unsigned char num) { P0 = seg_table[num]; // 段码输出 P2_1 = 0; // 位选使能(低电平) } // 定时器0中断服务 void timer0_isr() interrupt 1 { // 重装定时值 TH0 = (unsigned char)(timer0_reload[index] >> 8); TL0 = (unsigned char)(timer0_reload[index] & 0xFF); P3_2 = ~P3_2; // 翻转P3.2(主方波) // 处理P2.4脉冲 if (pulse_enable) { P2_4 = ~P2_4; // 翻转P2.4 pulse_count++; if (pulse_count >= 20) { // 10周期(20次翻转) pulse_enable = 0; P2_4 = 0; // 停止后置低电平 } } } // 初始化函数 void init() { // 定时器0配置 TMOD &= 0xF0; // 清除设置 TMOD |= 0x01; // 模式1(16位定时) AUXR |= 0x80; // 1T模式 TH0 = (unsigned char)(timer0_reload[index] >> 8); TL0 = (unsigned char)(timer0_reload[index] & 0xFF); ET0 = 1; // 允许中断 TR0 = 1; // 启动定时器 // IO口配置 P3M0 = 0x04; P3M1 = 0x00; // P3.2推挽输出 P2M0 = 0x12; P2M1 = 0x00; // P2.1/P2.4推挽输出 P0M0 = 0xFF; P0M1 = 0x00; // P0口推挽输出 P1M0 = 0x00; P1M1 = 0x00; // P1口准双向 // 外中断0配置(按键) IT0 = 1; // 下降沿触发 EX0 = 1; // 允许中断 EA = 1; // 总中断 // 初始显示 display_number(index + 1); } // 按键中断服务 void ex0_isr() interrupt 0 { delay_ms(10); // 消抖 if (P1_0 == 0) { index = (index + 1) % 8; // 循环切换频率 display_number(index + 1); pulse_enable = 1; // 重启脉冲 pulse_count = 0; while(!P1_0); // 等待按键释放 } } void main() { init(); while(1) { /* 主循环空转,中断处理 */ } } ``` #### 4. 关键实现说明 1. **定时器精度**:使用1T模式提升定时精度,非整数频率取整处理 2. **脉冲控制**:每周期翻转2次,20次翻转对应10个完整周期 3. **按键响应**:外中断0实现即时响应,消抖处理防误触发 4. **数码管驱动**: - 74HC245提升P0口驱动能力 - 静态显示降低CPU占用 5. **IO配置**: - 方波引脚设为推挽输出 - 按键引脚准双向模式 #### 5. 调试要点 1. 测量P3.2输出频率,验证是否匹配设定值 2. 用示波器观察P2.4脉冲数是否为10个 3. 检查数码管显示值与当前频率序号一致性 4. 若频率误差较大,可微调重装值补偿中断延迟
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值