《圈圈教你玩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的读写时序图

《圈圈教你玩USB》 第二章 USB 硬件系统设计_测试程序的编写和调试——看书笔记_第1张图片
分析: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 无硬件流控制)将会显示如下所示信息。
其中日期和时间可能不一样,这取决于当前的编译时间。
《圈圈教你玩USB》 第二章 USB 硬件系统设计_测试程序的编写和调试——看书笔记_第2张图片

2.12 本章小结

   本章内容的必要性
















你可能感兴趣的:(协议-,USB)