1、单片机最小系统
电源
单片机中常见5v和3.3v的单片机,“5v”和“3.3v”分别只是他们正常工作的典型值,5v和3.3v单片机也是以他们正常工作典型值命名的,他们正常工作电压的标准范围要根据单片机手册查询,
晶振
晶振分无源晶振和有源晶振。实物图和原理图见下图。
无源晶振: 依靠单片机内部振荡电路才能工作,接到单片机两个振荡引脚即可,晶体两个引脚无区别,电压无要求,两侧通常有电容,手册有要求根据手册选电容,手册无要求一般选20pf。
有源晶振: 无需依靠单片机内部振荡电路,只需外部供电达到电压要求,即可产生振荡频率,接到单片机晶振输入引脚即可接受到晶振频率,单片机晶振输出引脚无需连接。
两者区别:无源晶振信号质量和精度比有源晶振差,价格比有源晶振便宜。
复位电路:
KST-51开发板复位电路如下图
上电复位:一上电,给电容充电,此时电容相当于导线,RST高电平,电容充电越多,电路电流越来越小直到电容开路电流为0,RST电压越来越小直到低电平(0v),这就是上电复位,复位时间达到要求即可复位。
手动(按键)复位:按键未按前,RST为低电平,按键按下,RST为高电平,松开按键,电源给电容充电后,电容开路,RST为低电平。18欧姆电阻作用:为抑制按键按下后,电容产生的电磁干扰。
2、独立按键和准双向IO口
用户和单片机交流信息依赖于输入设备和输出设备,前边LED小灯部分都是输出设备,现在来学习输入设备-----按键。
按键:按键电路分独立式按键和矩阵式按键两种。下面说明独立式按键,见下图原理图。
说明:四条KeyIn编号输入线连接单片机IO口,当按键k1按下,KeyIn1引脚为低电平,当按键k1松开,KeyIn1引脚为高电平,KeyIn编号IO口的电平情况由按键的状态所决定。
准双向IO口:在KST51-开发板中,按键接到P2中KeyIn编号IO口上,这些IO口上电默认是准双向IO口,下面来学习准双向IO口电路,如下图8-7。
说明:这种IO口,有输出端和输入端,要正常读取外部信号状态,必须要保证自己内部输出的是1。当内部输出为1时,经过非门后输出为0,三极管不导通,按键松开,内部输入为高电平,按键按下,内部输入为低电平。而当内部输出为0时,通过分析,按键是什么状态,内部输入都为低电平。
3、矩阵按键
矩阵按键相对于独立按键而言可以减少IO口的使用,原理图见下图。
说明:如果keyout1输出为低电平,相当于GND,而keyout2、keyout3、keyout4输出为高电平的时候,K1、K2、K3、K4相当于独立按键。
4、矩阵按键扫描程序
//矩阵按键的扫描
#include sbit ADDR0 = P1^0; sbit ADDR1 = P1^1; sbit ADDR2 = P1^2; sbit ADDR3 = P1^3; sbit ENLED = P1^4; sbit keyin1 = P2^4; sbit keyin2 = P2^5; sbit keyin3 = P2^6; sbit keyin4 = P2^7; sbit keyout1 = P2^3; sbit keyout2 = P2^2; sbit keyout3 = P2^1; sbit keyout4 = P2^0; unsigned char code LedChar[] = { //数码管显示字符转换表 0xC0, 0xF9, 0xA4, 0xB0, 0x99, 0x92, 0x82, 0xF8, 0x80, 0x90, 0x88, 0x83, 0xC6, 0xA1, 0x86, 0x8E }; unsigned char keysta[4][4]= { //全部矩阵按键的当前状态 {1,1,1,1},{1,1,1,1},{1,1,1,1},{1,1,1,1}//数组不能定义成bit型 }; void main() { unsigned char i, j; unsigned char backup[4][4] = {//按键值备份,保存前一次的值 {1,1,1,1},{1,1,1,1},{1,1,1,1},{1,1,1,1} }; EA = 1;//使能总中断 ENLED = 0;//选择数码管DS0显示 ADDR3 = 1; ADDR2 = 0; ADDR1 = 0; ADDR0 = 0; TMOD = 0x10; //设置T1为模式1 TH1 = 0xFC; //为T1赋值,定时1ms TL1 = 0x67; TR1 = 1; //开启T1 ET1 = 1; //使能T1中断 P0 = LedChar[0]; //默认数码管显示0 while(1) { for(i = 0; i < 4; i++) //循环检测4*4矩阵按键 { for(j = 0; j < 4; j++) { if(keysta[i][j] != backup[i][j]) //检测按键动作 { if(backup[i][j] == 0) //前一次值为0,说明当前按键弹起 { P0 = LedChar[4*i+j];//将编号显示到数码管 } backup[i][j] = keysta[i][j];//更新前一次备份值 } } } } } void Interrupt() interrupt 3 { unsigned char i; static unsigned char keyout = 0; //矩阵按键扫描输出索引 static unsigned char keybuf[4][4] = { //矩阵按键扫描缓冲区 {0xFF,0xFF,0xFF,0xFF}, {0xFF,0xFF,0xFF,0xFF}, {0xFF,0xFF,0xFF,0xFF}, {0xFF,0xFF,0xFF,0xFF} }; TH1 = 0xFC; //重新为T1赋值 TL1 = 0x67; //将一行的4个按键值移入缓冲区 keybuf[keyout][0] = (keybuf[keyout][0] << 1) | keyin1; keybuf[keyout][1] = (keybuf[keyout][1] << 1) | keyin2; keybuf[keyout][2] = (keybuf[keyout][2] << 1) | keyin3; keybuf[keyout][3] = (keybuf[keyout][3] << 1) | keyin4; //消抖后更新按键状态 for(i = 0; i < 4; i++) //每行四个按键,所以循环四次 { if((keybuf[keyout][i] & 0x0F) == 0x00) { //连续扫描4次值为0,即4*4ms内都是按下状态时,可认为按键已稳定按下 keysta[keyout][i] = 0; }else if((keybuf[keyout][i] & 0x0F) == 0x0F) { //连续扫描4次值为1,即4*4ms内都是弹起状态时,可认为按键已稳定弹起 keysta[keyout][i] = 1; } } //执行一下次的扫描输出 keyout++; //输出索引递增 keyout = keyout & 0x03;//索引值加到4即归零 switch(keyout) //根据索引,释放当前输出引脚,拉低下次的输出引脚 { case 0: keyout4 = 1; keyout1 = 0; break; case 1: keyout1 = 1; keyout2 = 0; break; case 2: keyout2 = 1; keyout3 = 0; break; case 3: keyout3 = 1; keyout4 = 0; break; default:break; } } 原理:根据上面所讲的内容和下图单片机原理图的分析,程序原理很好理解,程序利用动态扫描扫描4行按键,每隔一秒扫描一行按键,通过检测不同位置按键输入引脚的电平变化,来改变数码管的值,程序中keyin输入和keyout输出颠倒是为了让输出信号有足够时间来稳定。 5、按键抖动 按键抖动:按键闭合时间由操作人员控制决定,通常会在100ms以上,刻意快速的按,也会在40-50ms左右,因为按键有机械弹性,按下和弹起瞬间都会有一连串的抖动,如下图8-10,这种抖动直接影响了数码管值的变化,上面程序运用了软件消抖解决该问题,每个按键每隔4ms检测一次按键状态,每个按键检测4次,每个按键检测4*4ms,若4次状态相同则可确定按键的状态。这就将按键的抖动消去了。 6、综合小程序-简易加减法计算器 //支持“向上键”加,“向下键”减, 回车计算结果,但不支持连续加减操作 #include sbit ADDR0 = P1^0; sbit ADDR1 = P1^1; sbit ADDR2 = P1^2; sbit ADDR3 = P1^3; sbit ENLED = P1^4; sbit keyin1 = P2^4; sbit keyin2 = P2^5; sbit keyin3 = P2^6; sbit keyin4 = P2^7; sbit keyout1 = P2^3; sbit keyout2 = P2^2; sbit keyout3 = P2^1; sbit keyout4 = P2^0; void KeyDriver();//按键驱动函数 void KeyAction(unsigned char keycodeMap);//按键动作函数 void ShowNumber(signed long num);//显示函数 void Keyscan();//按键扫描函数 void LedScan();//数码管扫描函数 unsigned char code LedChar[] = { //数码管显示字符转换表 0xC0, 0xF9, 0xA4, 0xB0, 0x99, 0x92, 0x82, 0xF8, 0x80, 0x90, 0x88, 0x83, 0xC6, 0xA1, 0x86, 0x8E }; unsigned char Ledbuff[6] = { //数码管显示缓冲区 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }; unsigned char code KeyCodeMap[4][4] = { //矩阵按键编号到标准键盘键码的映射表 { 0x31, 0x32, 0x33, 0x26 }, //数字键1、数字键2、数字键3、向上键 { 0x34, 0x35, 0x36, 0x25 }, //数字键4、数字键5、数字键6、向左键 { 0x37, 0x38, 0x39, 0x28 }, //数字键7、数字键8、数字键9、向下键 { 0x30, 0x1B, 0x0D, 0x27 } //数字键0、ESC键、 回车键、 向右键 }; unsigned char keysta[4][4]= { //全部矩阵按键的当前状态 {1,1,1,1},{1,1,1,1},{1,1,1,1},{1,1,1,1} }; void main() { ENLED = 0; //使能U3 ADDR3 = 1; EA = 1; //使能总中断 TMOD = 0x01;//设置T0为模式1 TH0 = 0xFC;//为T0赋值,定时1ms TL0 = 0x67; ET0 = 1; //使能T0中断 TR0 = 1; //开启T0 Ledbuff[0] = LedChar[0]; //上电显示0 while(1) { KeyDriver();//调用按键驱动函数 }; } //按键驱动函数,检测按键动作,调度相应动作函数,需在主循环中调用 void KeyDriver() { unsigned char i, j; static unsigned char backup[4][4]= { //按键备份值,保存前一次的值 {1,1,1,1},{1,1,1,1},{1,1,1,1},{1,1,1,1} }; for(i = 0; i < 4; i++) //循环检测4*4的矩阵按键 { for(j = 0; j < 4; j++) { if(backup[i][j] != keysta[i][j]) //检测按键动作 { if(backup[i][j] != 0) //按键按下时执行动作 { KeyAction(KeyCodeMap[i][j]); //调用按键动作函数 } backup[i][j] = keysta[i][j]; //刷新前一次的备份值 } } } } //按键动作函数,根据键码执行相应的操作,keycode-按键按码 void KeyAction(unsigned char keycodeMap) { static signed char oprt = 0; //用于保存加减运算符 static signed long addend = 0;//用于保存输入的减数 static signed long result = 0;//用于保存运算结果 if((keycodeMap >= 0x30) && (keycodeMap <= 0x39))//输入0~9的数字 { addend = (addend * 10) + (keycodeMap - 0x30);//整体十进制左移,新数字进入个位 ShowNumber(addend); //运算结果显示到数码管 } else if(keycodeMap == 0x26) //向上键用作加号,执行连法运算 { oprt = 0; //设置运算符变量 result = addend; //运算数存到结果中,准备进行加减 addend = 0; //清零运算数,准备接收下一个运算数 ShowNumber(result); //刷新数码管显示 } else if(keycodeMap == 0x28) //向下键用作减号,执行减法运算 { oprt = 1;//设置运算符变 result = addend;//运算数存到结果中,准备进行加减 addend = 0; //清零运算数,准备接收下一个运算数 ShowNumber(result); //刷新数码管显示 } else if(keycodeMap == 0x0D) //回车键执行加减法运算) { if(oprt == 0) //执行加法运算 { result += addend; } else //执行减法运算 { result -= addend; } addend = 0; ShowNumber(result); //运算结果显示到数码管 } else if(keycodeMap == 0x1B)//ESC键,清零结果 { result = 0; addend = 0; ShowNumber(addend); //清零后的减数显示到数码管 } } //将一个无符号长整形的数字显示到数码管上,num为待显示数字 void ShowNumber(signed long num) { unsigned char sign = 0; unsigned char buf[6]; signed char i; if(num < 0) //首先提取并暂存符号位 { sign = 0; num = -num; } else{ sign = 1; } for(i = 0; i < 6; i++) //把长整型数转换为6位十进制的数组 { buf[i] = num % 10; num = num / 10; } for(i = 5; i >= 1; i--) //从最高位起,遇到0转换为空格,遇到非0则退出循环 { if(buf[i] == 0) { Ledbuff[i] = 0xFF; } else { break; } } if(sign == 0) //负数时,需在最前面添加负号 { if(i < 5) //当有效位数小于6位时添加负号,否则显示结果将是错的 { Ledbuff[i+1] = 0xBF; } } for(; i >= 0; i--) //剩余低位都如实转换为数码管显示字符 { Ledbuff[i] = LedChar[buf[i]]; } } void InterruptTimer0() interrupt 1 { TH0 = 0xFC;//为重新T0赋值 TL0 = 0x67; LedScan(); //调用数码管显示扫描函数 Keyscan(); //调用按键扫描函数 } void Keyscan()//按键扫描函数 { unsigned char i; static unsigned char keyout = 0;//矩阵按键扫描输出索引 static unsigned char keybuf[4][4] = { //矩阵按键扫描缓冲区 {0xFF,0xFF,0xFF,0xFF}, {0xFF,0xFF,0xFF,0xFF}, {0xFF,0xFF,0xFF,0xFF}, {0xFF,0xFF,0xFF,0xFF} }; //将一行的4个按键值移入缓冲区 keybuf[keyout][0] = (keybuf[keyout][0] << 1) | keyin1; keybuf[keyout][1] = (keybuf[keyout][1] << 1) | keyin2; keybuf[keyout][2] = (keybuf[keyout][2] << 1) | keyin3; keybuf[keyout][3] = (keybuf[keyout][3] << 1) | keyin4; //消抖后更新按键状态 for(i = 0; i < 4; i++) //每行4个按键,所以循环四次 { if((keybuf[keyout][i] & 0x0F) == 0x00) { //连续4次扫描值为0,即4*4ms内都是按下状态,可认为按键以稳定地按下 keysta[keyout][i] = 0; } else if((keybuf[keyout][i] & 0x0F) == 0x0F) { //连续4次扫描值为1,即4*4ms内都是弹起状态,可认为按键以稳定地弹起 keysta[keyout][i] = 1; } } //执行下一次的扫描输出 keyout++; //输出索引递增 keyout = keyout & 0x03; //索引值加到4后归零 switch(keyout) //根据索引,释放当前输出引脚,拉低下次的输出引脚 { case 0:keyout4 = 1; keyout1 = 0; break; case 1:keyout1 = 1; keyout2 = 0; break; case 2:keyout2 = 1; keyout3 = 0; break; case 3:keyout3 = 1; keyout4 = 0; break; default:break; } } //数码管动态扫描刷新函数,须需在定时中断中调用 void LedScan() { static unsigned char i = 0; //动态扫描的索引 P0 = 0xFF; //显示消隐 switch(i) { case 0: ADDR2 = 0; ADDR1 = 0; ADDR0 = 0; i++; P0 = Ledbuff[0];break; case 1: ADDR2 = 0; ADDR1 = 0; ADDR0 = 1; i++; P0 = Ledbuff[1];break; case 2: ADDR2 = 0; ADDR1 = 1; ADDR0 = 0; i++; P0 = Ledbuff[2];break; case 3: ADDR2 = 0; ADDR1 = 1; ADDR0 = 1; i++; P0 = Ledbuff[3];break; case 4: ADDR2 = 1; ADDR1 = 0; ADDR0 = 0; i++; P0 = Ledbuff[4];break; case 5: ADDR2 = 1; ADDR1 = 0; ADDR0 = 1; i=0; P0 = Ledbuff[5];break; default:break; } } 说明:程序功能上只能进行一次加或一次减,该程序分为几个模块,目的是为了让程序层次化。
上一篇:【自学51单片机】9 -- 步进电机原理、蜂鸣器原理,单片机IO
下一篇:【自学51单片机】7--LED点阵学习
推荐阅读
史海拾趣
在21世纪初,随着电子行业的飞速发展,对于高性能磁性元件的需求也日益增长。Ferroxcube公司,作为世界第二大磁性元件生产商,凭借其卓越的技术和产品质量,迅速在市场上占据了一席之地。公司不断投入研发,推出了一系列创新的磁芯和磁环产品,深受客户好评。
在一次重要的行业展会上,Ferroxcube展示了一款新型的高性能磁芯,其优越的性能和稳定性吸引了众多行业内人士的关注。一家知名的电源制造商对这款产品表现出了浓厚的兴趣,并表达了合作的意愿。经过几轮深入的洽谈,双方达成了战略合作协议,Ferroxcube的磁芯成为了这家电源制造商的首选元件。
这一合作不仅为Ferroxcube带来了大量的订单,还提升了公司在行业内的知名度和影响力。公司借此机会加大了研发投入,进一步巩固了其在磁性元件领域的领先地位。
为了进一步扩大市场份额,Catalyst公司积极实施国际化战略。公司通过与国外知名企业的合作与交流,引进先进的技术和管理经验,不断提升自身的竞争力。同时,Catalyst还积极参加国际电子展会和论坛,展示公司的最新产品和技术成果,吸引了众多国际客户的关注。通过国际化战略的拓展,Catalyst成功打开了海外市场的大门,为公司的长远发展奠定了坚实的基础。
作为淄博市的重大项目,宝乘公司得到了各级政府的大力支持。这些支持不仅体现在资金扶持上,还包括政策优惠、人才引进等多个方面。在政府的助力下,宝乘公司得以快速发展,不断壮大。同时,公司也积极响应政府的号召,参与新旧动能转换等重大项目,为地方经济的发展做出了贡献。
近年来,随着全球对环保和可持续发展的关注日益增加,伊顿公司也积极响应这一趋势,将其技术与绿色能源相结合。伊顿的EX-DMi型电容器金属封闭柜、SCB型环氧浇注干式变压器等产品,在新能源大基地建设中得到了广泛应用。这些产品不仅具有高效、稳定的性能,还采用了无SF6绝缘技术等环保技术,有效降低了温室效应的影响。此外,伊顿还推出了可支持锂电系统的UPS等产品,为光伏和风电机组等关键部件提供安全稳定的供电保障。
ECLIPTEK公司自创立之初就专注于高精度电子元件的研发与生产。面对激烈的市场竞争,公司不断投入研发资源,推出了一系列具有创新性的产品,如高精度时间同步模块和低功耗传感器。这些产品凭借其卓越的性能和可靠性,迅速在市场上赢得了良好的口碑,使ECLIPTEK成为电子元件行业的佼佼者。
奥松电子自成立以来,一直致力于MEMS特色半导体芯片的研发与生产。公司凭借其国内领先的生产线技术,成功打造了一条高效、稳定的芯片生产线。这不仅提升了公司的生产效率,也为公司在电子行业中树立了良好的口碑。随着技术的不断进步,奥松电子的芯片性能也得到了显著提升,满足了市场对于高质量、高性能芯片的需求。
传感器测量水温,并以此为反馈值控制加热水温(使用PID算法), 使水温可以保持在一个稳定的范围内,同时显示水温变化数据。 要求学生掌握单片机的工作原理,控制热水的温度为30oC, 允许的变化范围为正负2oC。要求设计出合理的控制硬件电路, 有 ...… 查看全部问答∨ |
|
本来一直在dos用build -c编译也没问题 ,今天在pb下用编译bsp,然后用 build -c编译eboot就产生这样的错误了 return E:\\WINCE500\\PLATFORM\\SMDK2410\\SRC\\BOOTLOADER\\EBOOT\\.\\nand.c BUILD: [01:0000000056:INFO ] ...… 查看全部问答∨ |
|
接串口的设备是红外接收模块。 因为驱动的工作流程: handle_scancode-->|tty_queue|-->|tty_ldisc|-->/dev/ttyX 而红外接收模块的通讯协议略不同于串口的通讯协议。 所以,串口接收到的数据是需要正确解析。 为此类的设备写tty驱动,可 ...… 查看全部问答∨ |
从网上下载的一篇文章,原文对电路图的介绍“为实现快速 PWM 充电方式,电路采用并联充、 放电的拓扑结构,即充电控制管 M1 和放电控制管 M2 与蓄电池采用并联方式,在选择开关管时要综合考虑太阳能电池和负载的功率,以及可靠性和带载能力等因素,故选 ...… 查看全部问答∨ |
香主,请教一个SysTick例程的问题:/* SysTick end of count event each 1ms with input clock equal to 9MHz (HCLK/8, default) */ SysTick_Se ...… 查看全部问答∨ |
|
没玩过MSP430,正好有个设计任务,申请后很快得到回复,试验装到达后发现东西很精致,不错!首先在TI官方网站了解了MSP430-RF430的特性以及用途,及其使用现状。然后直接进入SimpliciTI协议的研究,SimpliciTI是很简单的LISTEN(),LINKTO()模式,END ...… 查看全部问答∨ |