《圈圈教你玩USB》 第二章 USB 硬件系统设计_测试程序的编写和调试——看书笔记

2.11 测试程序的编写和调试

2.11.4 按键驱动的编写

考虑到以后程序的方便性,这里使用定时器中断方式来扫描按键。定时器每隔5ms中断一次,即每5ms
扫描一次键盘。

key.c

    1)先初始化好一个定时器,此处选择定时器0作为按键扫描的定时器,而定时器1留作串口波特率发生器。
初始化定时器就是设置定时器的工作模式,启动定时器等。
   
   
  1. //定时器0初始化,用来做按键扫描
  2. void InitTimer0(void)
  3. {
  4. TMOD&=0xF0; //定时器低4位是控制定时器0的,先置0
  5. TMOD|=0x01; //然后将最低位置1,最终选择16位定时器模式
  6. ET0=1; //允许定时器0中断
  7. TR0=1; //启动定时器0
  8. }
    2)再初始化键盘
    
    
  1. volatile uint8 idata KeyCurrent,KeyOld,KeyNoChangedTime;
  2. volatile uint8 idata KeyPress;
  3. volatile uint8 idata KeyDown,KeyUp,KeyLast;
  4. volatile uint8 KeyCanChange;
  5. //函数功能:键盘初始化
  6. void InitKeyboard(void)
  7. {
  8. KeyIO=0xFF; //键盘对应的口设置为输入状态
  9. KeyPress=0; //无按键按下
  10. KeyNoChangedTime=0;
  11. KeyOld=0;
  12. KeyCurrent=0;
  13. KeyLast=0;
  14. KeyDown=0;
  15. KeyUp=0;
  16. InitTimer0(); //初始化定时器
  17. KeyCanChange=1; //允许键值改变
  18. }
    KeyCurrent、KeyOld、KeyLast和KeyNoChangedTime是扫描按键使用的变量,应用程序不直接使用它们。
    KeyPress、KeyDown、KeyUp、KeyCanChange是提供给应用程序使用的变量。
    KeyPress表示当前被按住不放的键;
    KeyDown表示新按下的键;
    KeyUp表示心松开的键;
    KeyCanChange是应用程序用来控制是否允许新的扫描。
    当某个按键被按下时,KeyPress对应位被置1,并且KeyDown对应位也置1;当按键松开后,KeyPress对应
位为0,KeyUp对应位被置1。
    3)定时器0中断处理
   
   
  1. //函数功能:定时器0中断处理。
  2. //22.1184M晶体约5ms中断一次。
  3. void Timer0Isr(void) interrupt 1
  4. {
  5. //定时器0重装,定时间隔为5ms,加15是为了修正重装所花费时间
  6. //这个值可以通过软件仿真来确定,在这里设置断点,调整使两次运行
  7. //时间差刚好为5ms即可。
  8. TH0=(65536-Fclk/1000/12*5+15)/256;
  9. TL0=(65536-Fclk/1000/12*5+15)%256;
  10. if(!KeyCanChange)return; //如果正在处理按键,则不再扫描键盘
  11. //开始键盘扫描
  12. //保存按键状态到当前按键情况
  13. //KeyCurrent总共有8个bit
  14. //当某个开关按下时,对应的bit为1
  15. KeyCurrent=GetKeyValue(); //读取键值,GetKeyValue()其实是个宏,不是函数,
  16. //这里故意写成函数的样子,美观。它的定义在
  17. //key.h文件中
  18. if(KeyCurrent!=KeyOld) //如果两次值不等,说明按键情况发生了改变
  19. {
  20. KeyNoChangedTime=0; //键盘按下时间为0
  21. KeyOld=KeyCurrent; //保存当前按键情况
  22. return; //返回
  23. }
  24. else
  25. {
  26. KeyNoChangedTime++; //按下时间累计
  27. if(KeyNoChangedTime>=1) //如果按下时间足够
  28. {
  29. KeyNoChangedTime=1;
  30. KeyPress=KeyOld; //保存按键
  31. KeyDown|=(~KeyLast)&(KeyPress); //求出新按下的键
  32. KeyUp|=KeyLast&(~KeyPress); //求出新释放的键
  33. KeyLast=KeyPress; //保存当前按键情况
  34. }
  35. }
  36. }

2.11.5 串口驱动的编写

1. 串口驱动的重要性:

    调试时,在对应位置打印出信息,可以知道程序走过哪些步骤;

2. usart.c

在使用串口之前,必须对串口进行初始化。
   
   
  1. //函数功能:串口初始化
  2. void InitUART(void)
  3. {
  4. EA=0;         //暂时关闭中断
  5. TMOD&=0x0F;  //定时器1模式控制在高4位
  6. TMOD|=0x20; //定时器1工作在模式2,自动重装模式
  7. SCON=0x50; //串口工作在模式1
  8. TH1=256-Fclk/(BitRate*12*16); //计算定时器重装值
  9. TL1=256-Fclk/(BitRate*12*16);
  10. PCON|=0x80; //串口波特率加倍
  11. ES=1; //串行中断允许
  12. TR1=1; //启动定时器1
  13. REN=1; //允许接收
  14. EA=1; //允许中断
  15. }
串口中断处理函数
   
   
  1. volatile uint8 Sending;
  2. //函数功能:串口中断处理。
  3. void UartISR(void) interrupt 4
  4. {
  5. if(RI) //收到数据
  6. {
  7. RI=0; //清中断请求,因为这里只发送,不接受,故此处只要清除中断标志即可
  8. }
  9. else //发送完一字节数据
  10. {
  11. TI=0;
  12. Sending=0; //清正在发送标志
  13. }
  14. }
发送单个字符函数
   
   
  1. //函数功能:往串口发送一字节数据。
  2. //入口参数:d: 要发送的字节数据。
  3. void UartPutChar(uint8 d)
  4. {
  5. SBUF=d; //将数据写入到串口缓冲
  6. Sending=1; //设置发送标志
  7. while(Sending); //等待发送完毕
  8. }
发送一个字符串函数
   
   
  1. //函数功能:发送一个字符串。
  2. //入口参数:pd:要发送的字符串指针。
  3. void Prints(uint8 * pd)
  4. {
  5. while((*pd)!='\0') //发送字符串,直到遇到0才结束
  6. {
  7. UartPutChar(*pd); //发送一个字符
  8. pd++;            //移动到下一个字符
  9. }
  10. }
以HEX格式发送一个整数
   
   
  1. code uint8 HexTable[]={'0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'};
  2. //函数功能:将短整数按十六进制发送。
  3. //入口参数:待发送的整数。
  4. void PrintShortIntHex(uint16 x)
  5. {
  6. uint8 i;
  7. uint8 display_buffer[7];
  8. display_buffer[6]=0;
  9. display_buffer[0]='0';
  10. display_buffer[1]='x';
  11. for(i=5;i>=2;i--) //将整数转换为4个字节的HEX值
  12. {
  13. display_buffer[i]=HexTable[(x&0xf)];
  14. x>>=4;
  15. }
  16. Prints(display_buffer);
  17. }

2.11.6 PDIUSBD12读写函数和读ID的实现

1. 读D12的ID号的目的

    知道D12是否正常工作

2. PDIUSBD12的读写时序图


分析:CS_N是片选信号。上图所示,只有片选信号拉低时,下面的操作才有意义。
          A0是地址线,用于选择是命令还是数据。A0为1时,表示操作的是命令;A0为0时,表示操作的是数据。
          WR_N是写信号,表示WR_N的上升沿将数据写入芯片中。数据必须在上升沿的前后稳定地保持一段时
   间(即图中的tWDSU和tWDH)才能可靠写入。
          RD_N是读选通信号,在读数据时,先将RD_N置低,等待tRLDD时间后,数据将出现在数据总线DATA
   [7:0]上,这时可以读取数据。读取完后,将RD_N拉高,数据在总线上的数据将在tRHDZ时间后消失。

3. PDIUSBD12.c

    根据上面的分析,可以得出:
   写命令的操作过程为:先将A0置高(即设置为命令状态),再讲WR_N置低,把需要发送的命令放到数据总线上,
再讲WR_N置高。这样将产生一个上升沿,从而把数据写入了D12中。写完后,须将总线设置为输入状态,以避免
总线冲突。
    写数据的操作过程为:只要将上面的A0设置为低即可(设置为数据状态)。
   
   
  1. //函数功能:D12写命令。
  2. //入口参数:Command:一字节命令。
  3. void D12WriteCommand(uint8 Command)
  4. {
  5. D12SetCommandAddr(); //设置为命令地址
  6. D12ClrWr();            //WR置低
  7. D12SetPortOut();       //将数据口设置为输出状态(注意这里为空宏,移植时可能有用)
  8. D12SetData(Command);  //输出命令到数据口上
  9. D12SetWr();            //WR置高
  10. D12SetPortIn();        //将数据口设置为输入状态,以备后面输入使用
  11. }
    读一个字节的操作过程为:先将A0置低(即设置为数据状态),再将RD_N置低(表示读数据),读取P0口上的数据
并保存,最后将RD_N置高,结束读过程。最后函数返回读取到的数据。
   
   
  1. //函数功能:读一字节D12数据。
  2. //返 回:读回的一字节。
  3. uint8 D12ReadByte(void)
  4. {
  5. uint8 temp;
  6. D12SetDataAddr(); //设置为数据地址
  7. D12ClrRd();        //RD置低
  8. temp=D12GetData(); //读回数据
  9. D12SetRd();        //RD置高
  10. return temp;       //返回读到数据
  11. }
    读取2字节的ID号
   
   
  1. //函数功能:读D12的ID。
  2. //返 回:D12的ID。
  3. uint16 D12ReadID(void)
  4. {
  5. uint16 id;
  6. D12WriteCommand(Read_ID);        //写读ID命令
  7. id=D12ReadByte();                //读回ID号低字节
  8. id|=((uint16)D12ReadByte())<<8;  //读回ID号高字节
  9. return id;
  10. }

4. 修改main函数

   在main.c中增加读取并显示ID号的代码,检验芯片是否焊接正确。

main.c

   
   
  1. /********************************************************************
  2. 函数功能:主函数。
  3. 入口参数:无。
  4. 返 回:无。
  5. 备 注:无。
  6. ********************************************************************/
  7. void main(void) //主函数
  8. {
  9. uint8 i;
  10. uint16 id;
  11. EA=1; //打开中断
  12. InitKeyboard(); //初始化按键
  13. InitUART(); //初始化串口
  14. for(i=0;i<16;i++) //显示信息
  15. {
  16. Prints(HeadTable[i]);
  17. }
  18. id=D12ReadID();
  19. Prints("Your D12 chip\'s ID is: ");
  20. PrintShortIntHex(id);
  21. if(id==0x1012)
  22. {
  23. Prints(". ID is correct! Congratulations!\r\n\r\n");
  24. }
  25. else
  26. {
  27. Prints(". ID is incorrect! What a pity!\r\n\r\n");
  28. }
  29. while(1) //死循环
  30. {
  31. LEDs=~KeyPress; //将按键结果取反后控制LED
  32. if(KeyDown) //有键按下
  33. { //处理按下的键
  34. if(KeyDown&KEY1)
  35. {
  36. Prints("KEY1 down\r\n");
  37. KeyDown&=~KEY1;
  38. }
  39. if(KeyDown&KEY2)
  40. {
  41. Prints("KEY2 down\r\n");
  42. KeyDown&=~KEY2;
  43. }
  44. if(KeyDown&KEY3)
  45. {
  46. Prints("KEY3 down\r\n");
  47. KeyDown&=~KEY3;
  48. }
  49. if(KeyDown&KEY4)
  50. {
  51. Prints("KEY4 down\r\n");
  52. KeyDown&=~KEY4;
  53. }
  54. if(KeyDown&KEY5)
  55. {
  56. Prints("KEY5 down\r\n");
  57. KeyDown&=~KEY5;
  58. }
  59. if(KeyDown&KEY6)
  60. {
  61. Prints("KEY6 down\r\n");
  62. KeyDown&=~KEY6;
  63. }
  64. if(KeyDown&KEY7)
  65. {
  66. Prints("KEY7 down\r\n");
  67. KeyDown&=~KEY7;
  68. }
  69. if(KeyDown&KEY8)
  70. {
  71. Prints("KEY8 down\r\n");
  72. KeyDown&=~KEY8;
  73. }
  74. }
  75. if(KeyUp)//有键释放
  76. {//处理释放的键
  77. if(KeyUp&KEY1)
  78. {
  79. Prints("KEY1 up\r\n");
  80. KeyUp&=~KEY1;
  81. }
  82. if(KeyUp&KEY2)
  83. {
  84. Prints("KEY2 up\r\n");
  85. KeyUp&=~KEY2;
  86. }
  87. if(KeyUp&KEY3)
  88. {
  89. Prints("KEY3 up\r\n");
  90. KeyUp&=~KEY3;
  91. }
  92. if(KeyUp&KEY4)
  93. {
  94. Prints("KEY4 up\r\n");
  95. KeyUp&=~KEY4;
  96. }
  97. if(KeyUp&KEY5)
  98. {
  99. Prints("KEY5 up\r\n");
  100. KeyUp&=~KEY5;
  101. }
  102. if(KeyUp&KEY6)
  103. {
  104. Prints("KEY6 up\r\n");
  105. KeyUp&=~KEY6;
  106. }
  107. if(KeyUp&KEY7)
  108. {
  109. Prints("KEY7 up\r\n");
  110. KeyUp&=~KEY7;
  111. }
  112. if(KeyUp&KEY8)
  113. {
  114. Prints("KEY8 up\r\n");
  115. KeyUp&=~KEY8;
  116. }
  117. }
  118. }
  119. }

如果一切正确,在串口调试助手上(波特率9600 数据位8 停止位1 无硬件流控制)将会显示如下所示信息。
其中日期和时间可能不一样,这取决于当前的编译时间。

2.12 本章小结

   本章内容的必要性
















### 回答1: 《圈圈USB第二版PDF》是一本介绍USB接口的电子书籍,主要内容包括USB接口的原理、开发以及实际应用等方面。USB接口是现代计算机应用领域中广泛采用的接口之一。根据资料显示,截止2019年,全球USB接口市场已经超过了250亿美元。可见USB接口的重要性。 这本书是由知名的技术作家圈圈编写,十分适合初学者进行学习。书中主要从USB接口的背景和基本知识出发,详细介绍了USB接口的不同类型,速率、内部结构以及数据传输原理等。同时,书中也详细讲述了USB接口的应用范围和相关应用场景,这些知识都是十分实用的。 此外,书中还介绍了USB接口的开发工具和开发环境,通过实际的代码演示,读者可以更快地了解USB接口的开发流程,掌握相关开发技巧和方法。书中提供了非常详尽的开发案例,对于初学者来说非常实用。 总之, 《圈圈USB第二版PDF》 是一本非常好的USB接口学习资料。它结构清晰,知识详尽,实用性强,对于USB接口的初学者和开发者都是一本不可缺少的指南。对于想要深入了解USB接口的读者来说,此书肯定会满足他们的需求。 ### 回答2: 《圈圈USB第二版PDF》是一本介绍如何使用USB的电子书。本书主要包含以下内容: 首先,本书介绍了USB的基本知识。USB(Universal Serial Bus,通用串行总线)是一种用于连接计算机和外部设备的接口标准。本书会解释USB的起源、发展和使用场景,帮助读者了解USB的基本原理和技术规范。 其次,本书详细介绍了USB的连接与配置。USB设备通常需要通过连接线与计算机进行连接。本书会讲解如何正确连接USB设备,并介绍USB接口的不同类型与特点。此外,本书还会读者如何正确配置USB设备,以便计算机能够识别并正确地与设备进行通信。 接着,本书还会涵盖USB设备的驱动程序安装与升级。USB设备在计算机上正常工作需要相应的驱动程序。本书将详细讲解如何正确安装并更新USB设备的驱动程序,以确保设备能够与计算机良好兼容。 最后,本书还将介绍一些常见的USB问题排除方法。例如,当USB设备无法正常连接、无法识别或无法工作时,本书会提供一些解决方案和常见问题的排除方法,帮助读者解决USB设备使用中的一些常见问题。 《圈圈USB第二版PDF》是一本非常实用的电子书,对于想要学习如何正确使用USB设备以及解决USB设备使用中可能遇到的问题的读者来说,是一本非常有价值的工具书。无论是对于普通用户还是计算机专业人士来说,都能够从中获得很多有益的信息和指导。 ### 回答3: 《圈圈 USB 第二版 PDF》是一本关于如何使用 USB(通用串行总线)设备的电子书。 USB 是一种广泛使用的计算机接口,用于连接各种外部设备,例如打印机、相机、存储设备等等。这本电子书的主要目的是向读者展示如何正确地使用 USB 设备,以及如何解决使用过程中可能遇到的问题。 首先,电子书会介绍 USB 的基本知识,包括 USB 的历史、不同版本的 USBUSB 的工作原理等等。通过了解这些基础知识,读者可以更好地理解 USB 设备的工作原理和功能。 其次,电子书会详细介绍如何连接、安装和配置 USB 设备。这包括如何正确连接 USB 设备到计算机、如何安装和更新 USB 设备的驱动程序,以及如何配置 USB 设备的设置等等。这些步骤对于保证 USB 设备的正常工作非常重要。 此外,电子书还会涵盖一些常见问题和解决方案,例如 USB 设备无法被识别、USB 传输速度慢等等。通过学习这些解决方案,读者可以在遇到问题时快速找到解决方法。 最后,电子书也会展示一些有关 USB 应用的实用技巧和建议。例如如何安全地拔下 USB 设备、如何优化 USB 设备的性能等等。这些技巧可以帮助用户更好地利用 USB 设备。 总而言之,《圈圈 USB 第二版 PDF》是一本帮助读者学习和掌握 USB 设备的使用技巧的电子书。通过阅读本书,读者可以更好地理解 USB 设备的工作原理、正确地使用和配置 USB 设备,并解决使用过程中可能遇到的问题。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值