• 单片机实验(二)


    前言

    实验一:用AT89C51单片机控制LCD 1602,使其显示两行文字,分别显示自己的学号和姓名拼音。

    实验二:设计一个中断嵌套程序。要求K1和K2都未按下时,单片机控制8只数码管,滚动输出完整的学号。当按一下K1时,产生一个低优先级的外部中断0请求(负跳变触发),进入外部中断0中断服务程序,数码管显示学号中的年份3秒以上。此时按一下K2时,产生一个高优先级的外部中断1请求(负跳变触发),进入外部中断1中断服务程序,是数码管显示学号的后三位持续3秒钟。当显示3秒之后,再从外部中断1返回继续执行外部中断0为低优先级,外部中断1外高优先级。

    实验三:36层的电梯控制设计。

    参考链接

    LED数码管的静态显示与动态显示(Keil+Proteus)-CSDN博客

    字符型液晶显示器LCD 1602的显示控制(Keil+Proteus)-CSDN博客

    外中断的应用-CSDN博客

    我用proteus仿真时,一运行很多元件的命名会自动修改.怎么回事_百度知道 (baidu.com)

    keil编译错误KEY.c(44): error C141: syntax error near ‘unsigned’, expected ‘__asm’_syntax error near 'unsigned', expected '__asm_ONE_Day|的博客-CSDN博客

    Proteus+51单片机模拟电梯运行(含源程序) - 知乎 (zhihu.com)

    基于AT89C51单片机的简易电梯上下楼层间移动系统_柒月玖.的博客-CSDN博客

    矩阵键盘独立接口设计(Keil+Proteus)-CSDN博客

    Proteus设置网络标签_proteus怎么放置网络标号-CSDN博客

    【精选】51单片机入门——数码管_单片机数码管_倾晨灬雨曦的博客-CSDN博客 

    实验一

    Keil

    需要修改的地方就是将每行显示的字符进行替换即可,书上面有一个光标右移的命令,我这里进行了取消。

    1. #include
    2. #include //包含_nop_()空函数指令的头文件
    3. #define uchar unsigned char
    4. #define uint unsigned int
    5. #define out P0
    6. sbit RS=P2^0;//位变量
    7. sbit RW=P2^1;//位变量
    8. sbit E=P2^2;//位变量
    9. //函数声明部分
    10. void lcd_initial(void);//LCD初始化函数
    11. void check_busy(void);//检查忙标志位函数
    12. void write_command(uchar com);//写命令函数
    13. void write_data(uchar dat);//写数据函数
    14. void string(uchar ad,uchar *s);//显示字符串
    15. void delay(uint);//延时
    16. void main(void){
    17. lcd_initial();//对LCD初始化
    18. while(1){
    19. string(0x83,"202140200126");//显示第一行的字符
    20. string(0xC4,"Liu Jian");//显示第二行的字符
    21. delay(200);//延时
    22. write_command(0x01);//清屏
    23. delay(100);//延时
    24. }
    25. }
    26. //延时
    27. void delay(uint j){
    28. uchar i=250;
    29. for(;j>0;j--){
    30. while(--i);
    31. i=249;
    32. while(--i);
    33. i=250;
    34. }
    35. }
    36. //检查忙标志
    37. void check_busy(void){
    38. uchar dt;
    39. do{
    40. dt=0xff;//dt为变量单元,初值为0xff
    41. //RS=0,E=1时才可以读忙标志位
    42. E=0;
    43. RS=0;
    44. RW=1;
    45. E=1;
    46. dt=out;//out为P0口,P0口的状态送入dt中
    47. }while(dt&0x80);//如果忙标志位BF=1,继续循环检测,等待BF=0
    48. E=0;//BF=0,LCD 1602不忙,结束检测
    49. }
    50. //写命令
    51. void write_command(uchar com){
    52. check_busy();
    53. //按规定RS和E同时为0时,才可以写命令
    54. E=0;
    55. RS=0;
    56. RW=0;
    57. out=com;//将命令com写入P0口
    58. E=1;//写命令时,E应为正脉冲,即正跳变,所以前面先置E=0
    59. _nop_();//空操作1个机器周期,等待硬件反应
    60. E=0;//E由高电平变为低电平,LCD 1602开始执行命令
    61. delay(1);//延时,等待硬件反应
    62. }
    63. //写数据
    64. void write_data(uchar dat){
    65. check_busy();//检测忙标志位BF=1则等待,若BF=0,则可对LCD 1602写入命令
    66. E=0;//按规定写数据时,E应为正脉冲,所以先置E=0
    67. //按规定RS=1和RW=0时,才可以写入数据
    68. RS=1;
    69. RW=0;
    70. out=dat;//将数据”dat“从P0口输出,即写入LCD 1602
    71. E=1;//E产生正跳变
    72. _nop_();//空操作1个机器周期,等待硬件反应
    73. E=0;//E由高电平变为低电平,写数据操作结束
    74. delay(1);
    75. }
    76. //液晶显示器初始化函数
    77. void lcd_initial(void){
    78. write_command(0x38);//8位两行显示,5*7点阵字符
    79. _nop_();//空操作1个机器周期,等待硬件反应
    80. write_command(0x0C);//开整体显示,光标关,无闪烁
    81. _nop_();//空操作1个机器周期,等待硬件反应
    82. //write_command(0x05);//光标右移
    83. _nop_();//空操作1个机器周期,等待硬件反应
    84. write_command(0x01);//清屏
    85. delay(1);
    86. }
    87. //输出显示字符串
    88. void string(uchar ad,uchar *s){
    89. write_command(ad);
    90. while(*s>0){
    91. write_data(*s++);//输出字符串,且指针增1
    92. delay(100);
    93. }
    94. }

    Proteus

     这个实验的原理图,在书上以及前面的博客都有提到,不需要进行修改。

    实验需要的元器件

    运行结果 

    实验二

    Keil

    在试验一的基础上增加中断服务函数的代码,一个是显示学号中的年份,一个是显示学号的后三位,我这里是直接用到了一个数组来实现,外部中断0的服务函数是把年份在前面四个数码管显示,外部中断1的服务函数是将学号后面三位在前面三个数码管来显示,然后需要设置中断的优先级。

    这里要注意的是:

    在某些C编译器支持的C标准中,而keil支持的是ANSI C标准,该标准规定声明变量的位置应当在所有可执行语句之前。不然会导致变量没有定义而报错。

    还有一个就是这个时间的问题,他需要保持三秒之后回去,这个我不知道怎么计算,所以还是自己调试超过三秒钟就没有管了。

    1. #include
    2. #include
    3. #define uchar unsigned char
    4. #define uint unsigned int
    5. uchar code dis_code[]={0xA4,0xC0,0xA4,0xF9,0x99,0xC0,0xA4,0xC0,0xC0,0xF9,0xA4,0x82};//202140200126(共阳极段码表)
    6. uchar code wei_code[]={0x01,0x02,0x04,0x08,0x10,0x20,0x40,0x80};//对应输出的位置
    7. //延时
    8. void Delay(uint i){
    9. uint j;
    10. for(;i>0;i--)//晶体振荡器为12MHz,j的选择与晶体振荡器的频率有关
    11. for(j=0;j<333;j++){;}
    12. }
    13. void main(){
    14. uchar i,j=0x80;
    15. while(1){
    16. EA=1;//总中断允许
    17. EX0=1;//允许外部中断0
    18. EX1=1;//允许外部中断1
    19. IT0=1;//选择外部中断0为下降沿触发
    20. IT1=1;//选择外部中断1为下降沿触发
    21. PX0=0;//设置中断0为低优先级
    22. PX1=1;//设置中断1为高优先级
    23. for(i=0;i<12;i++){
    24. j=_crol_(j,1);//循环左移一位
    25. P0=dis_code[i];//P0口输出段码
    26. P2=j;//P2口输出位控码
    27. Delay(200);//延时
    28. }
    29. }
    30. }
    31. //外部中断0,显示学号中的年份3秒以上
    32. void int0() interrupt 0{
    33. uchar i,j=0;
    34. EX0=0;//禁止外部中断0
    35. while(1){
    36. for(i=0;i<4;i++){
    37. P0=dis_code[i];//P0口输出段码
    38. P2=wei_code[i];//P2口输出位控码
    39. Delay(200);//延时
    40. }
    41. j++;
    42. if(j==3)
    43. break;
    44. }
    45. EX0=1;//打开外部中断0
    46. }
    47. //外部中断1,显示学号中的后三位3秒以上
    48. void int1() interrupt 2{
    49. uchar i,j=0;
    50. EX1=0;//禁止外部中断1
    51. while(1){
    52. for(i=0;i<3;i++){
    53. P0=dis_code[i+9];//P0口输出段码
    54. P2=wei_code[i];//P2口输出位控码
    55. Delay(200);//延时
    56. }
    57. j++;
    58. if(j==4)
    59. break;
    60. }
    61. EX1=1;//打开外部中断1
    62. }

    Proteus

    实验需要的元器件:元器件都是之前用到过的。

    这个就是出现一个 元器件的参考值在仿真的时候自己发生改变,人为进行修改之后他又自己回去了,而且就是一直报错,说你器件的名称重复了。

    解决:查阅网上信息说是因为电脑的用户名称是中文的导致的,然后没说解决办法,我是直接删除元件的参考,就可以仿真了。

    原理图就是在实验一的基础上面添加两个中断要用的按钮即可,连接就参考中断的部分。

    实验三

    Keil

    这个不知道题目的意思,目前就实现了楼层的移动,就是从第一层到第十六层不是直接从1显示16,而是从1到16中间的数字也需要 一起显示。

    这样就是把前面矩阵键盘的程序修改一下就行了。

    1. #include
    2. #define uint unsigned int
    3. #define uchar unsigned char
    4. sbit L1=P1^0;//定义列
    5. sbit L2=P1^1;
    6. sbit L3=P1^2;
    7. sbit L4=P1^3;
    8. uchar code digit[]={0xC0,0xF9,0xA4,0xB0,0x99,0x92,0x82,0xF8,0x80,0x90};//共阳极字型码0~9
    9. uchar future_keyval=1;//保存电梯将要取到的楼层
    10. uchar previous_keyval=1;//保存之前电梯处在的楼层
    11. void Delay(uint i);//延时函数
    12. void key_scan(void);//矩阵键盘扫描函数
    13. void display(uchar i);//显示当前楼层函数
    14. void main(){
    15. while(1){
    16. key_scan();
    17. //电梯处在的楼层和将要去的楼层不一致,需要电梯变化
    18. if(future_keyval!=previous_keyval){
    19. //将去到的楼层比现在高,表示需要上楼
    20. while(future_keyval>previous_keyval){
    21. //逐层显示
    22. previous_keyval++;
    23. display(previous_keyval);
    24. }
    25. //将去到的楼层比现在低,表示需要下楼
    26. while(future_keyval
    27. //逐层显示
    28. previous_keyval--;
    29. display(previous_keyval);
    30. }
    31. }else{
    32. //一致表示不需要移动
    33. display(future_keyval);
    34. }
    35. }
    36. }
    37. //延时
    38. void Delay(uint i){
    39. uint j;
    40. for(;i>0;i--)//晶体振荡器为12MHz,j的选择与晶体振荡器的频率有关
    41. for(j=0;j<333;j++){;}
    42. }
    43. //矩阵键盘扫描函数
    44. void key_scan(void){
    45. uchar i,temp;
    46. P1=0xEF;//行扫描初值1110 1111(扫描P1^4)
    47. for(i=0;i<4;i++){//逐行为低,按行扫描,一共4行
    48. if(L1==0)future_keyval=i*4+1;//判断第一列有无键被按下
    49. if(L2==0)future_keyval=i*4+2;//判断第二列有无键被按下
    50. if(L3==0)future_keyval=i*4+3;//判断第三列有无键被按下
    51. if(L4==0)future_keyval=i*4+4;//判断第四列有无键被按下
    52. Delay(10);//延时
    53. temp=P1;//读入P1口的状态
    54. temp=temp|0x0F;//将P1^3~P1^0为1
    55. temp=temp<<1;//左移,准备扫描下一行
    56. temp=temp|0x0F;
    57. P1=temp;//为扫描下一行做准备
    58. }
    59. }
    60. //自己定义楼层显示函数
    61. void display(uchar i){
    62. switch(i){
    63. case 1:P0=digit[0];P2=digit[1];break;
    64. case 2:P0=digit[0];P2=digit[2];break;
    65. case 3:P0=digit[0];P2=digit[3];break;
    66. case 4:P0=digit[0];P2=digit[4];break;
    67. case 5:P0=digit[0];P2=digit[5];break;
    68. case 6:P0=digit[0];P2=digit[6];break;
    69. case 7:P0=digit[0];P2=digit[7];break;
    70. case 8:P0=digit[0];P2=digit[8];break;
    71. case 9:P0=digit[0];P2=digit[9];break;
    72. case 10:P0=digit[1];P2=digit[0];break;
    73. case 11:P0=digit[1];P2=digit[1];break;
    74. case 12:P0=digit[1];P2=digit[2];break;
    75. case 13:P0=digit[1];P2=digit[3];break;
    76. case 14:P0=digit[1];P2=digit[4];break;
    77. case 15:P0=digit[1];P2=digit[5];break;
    78. case 16:P0=digit[1];P2=digit[6];break;
    79. }
    80. Delay(150);//延时
    81. }

    Proteus

    所需要的器件

    元件名称Proteus关键字
    51单片机AT89C51
    复位按钮BUTTON
    蓝色数码管7SEG-COM-AN-BLUE
    红色数码管7SEG-COM-ANODE

    拓展

    老师没回我信息,我也不知道他是什么意思,然后就写着好玩对数字的移动进行了拓展。

    1. #include
    2. #include
    3. #define uint unsigned int
    4. #define uchar unsigned char
    5. sbit L1=P1^0;//定义列
    6. sbit L2=P1^1;
    7. sbit L3=P1^2;
    8. sbit L4=P1^3;
    9. sbit L5=P1^4;
    10. sbit L6=P1^5;
    11. sbit L7=P1^6;
    12. sbit L8=P1^7;
    13. uchar code digit[]={0xC0,0xF9,0xA4,0xB0,0x99,0x92,0x82,0xF8,0x80,0x90};//共阳极字型码0~9
    14. uchar future_keyval=1;//保存电梯将要取到的楼层
    15. uchar previous_keyval=1;//保存之前电梯处在的楼层
    16. void Delay(uint i);//延时函数
    17. void key_scan(void);//矩阵键盘扫描函数
    18. void display(uchar i);//显示当前楼层函数
    19. void main(){
    20. while(1){
    21. key_scan();
    22. //电梯处在的楼层和将要去的楼层不一致,需要电梯变化
    23. if(future_keyval!=previous_keyval){
    24. //将去到的楼层比现在高,表示需要上楼
    25. while(future_keyval>previous_keyval){
    26. //逐层显示
    27. previous_keyval++;
    28. display(previous_keyval);
    29. }
    30. //将去到的楼层比现在低,表示需要下楼
    31. while(future_keyval
    32. //逐层显示
    33. previous_keyval--;
    34. display(previous_keyval);
    35. }
    36. }else{
    37. //一致表示不需要移动
    38. display(future_keyval);
    39. }
    40. }
    41. }
    42. //延时
    43. void Delay(uint i){
    44. uint j;
    45. for(;i>0;i--)//晶体振荡器为12MHz,j的选择与晶体振荡器的频率有关
    46. for(j=0;j<333;j++){;}
    47. }
    48. //矩阵键盘扫描函数
    49. void key_scan(void){
    50. uchar i,temp;
    51. P1=0xFF;//列都置为1
    52. P3=0xFE;//逐行扫描
    53. for(i=0;i<8;i++){//逐行为低,按行扫描,一共4行
    54. if(L1==0)future_keyval=i*8+1;//判断第一列有无键被按下
    55. if(L2==0)future_keyval=i*8+2;//判断第二列有无键被按下
    56. if(L3==0)future_keyval=i*8+3;//判断第三列有无键被按下
    57. if(L4==0)future_keyval=i*8+4;//判断第四列有无键被按下
    58. if(L5==0)future_keyval=i*8+5;//判断第五列有无键被按下
    59. if(L6==0)future_keyval=i*8+6;//判断第六列有无键被按下
    60. if(L7==0)future_keyval=i*8+7;//判断第七列有无键被按下
    61. if(L8==0)future_keyval=i*8+8;//判断第八列有无键被按下
    62. Delay(10);//延时
    63. temp=P3;//读入P3口的状态
    64. P1=0xFF;
    65. P3=_crol_(temp,1);//需要采用循环左移,扫描下一行(不然会导致出现很多个0,低位补0)
    66. }
    67. }
    68. //自己定义楼层显示函数
    69. void display(uchar i){
    70. P0=digit[i/10];
    71. P2=digit[i%10];
    72. Delay(150);//延时
    73. }

     

    总结

    这次的实验比上次明显难很多,上次把程序稍微修改一下就行了,现在的话还需要对原理图进行添加器件。

  • 相关阅读:
    壳聚糖基原位水凝胶包载角膜缘干细胞/纳米壳聚糖骨形态发生蛋白水凝胶的研究制备
    2022杭电多校第三场 Two Permutations (dp, 哈希)
    高端无主灯设计灯光设计该如何布置射灯灯具?
    零基础如何入门到精通人工智能Pytorch, 深度学习,如何跟进AI领域的最新算法,如何读论文、找代码
    漏刻有时数据可视化大屏(16)数据指标KPI和柱图折线图混排
    恒生电子面试经验
    linux——(5 部分软件安装)
    网络问题定位工具记录
    AutoIt Window Info 使用方法
    战备器材管理系统-部队物资仓库管理系统
  • 原文地址:https://blog.csdn.net/weixin_64066303/article/details/134479372