您的位置:首页 > 其它

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

2017-03-15 21:40 429 查看


2.11测试程序的编写和调试


2.11.4按键驱动的编写

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

扫描一次键盘。


key.c

  1)先初始化好一个定时器,此处选择定时器0作为按键扫描的定时器,而定时器1留作串口波特率发生器。

初始化定时器就是设置定时器的工作模式,启动定时器等。

//定时器0初始化,用来做按键扫描

voidInitTimer0(void)

{

TMOD&=0xF0;//定时器低4位是控制定时器0的,先置0

TMOD|=0x01;//然后将最低位置1,最终选择16位定时器模式

ET0=1;//允许定时器0中断

TR0=1;//启动定时器0

}


  2)再初始化键盘

volatileuint8idataKeyCurrent,KeyOld,KeyNoChangedTime;

volatileuint8idataKeyPress;

volatileuint8idataKeyDown,KeyUp,KeyLast;


volatileuint8KeyCanChange;

//函数功能:键盘初始化

voidInitKeyboard(void)

{

KeyIO=0xFF;	//键盘对应的口设置为输入状态

KeyPress=0;	//无按键按下

KeyNoChangedTime=0;

KeyOld=0;

KeyCurrent=0;

KeyLast=0;

KeyDown=0;

KeyUp=0;

InitTimer0();//初始化定时器

KeyCanChange=1;//允许键值改变

}


  KeyCurrent、KeyOld、KeyLast和KeyNoChangedTime是扫描按键使用的变量,应用程序不直接使用它们。

  KeyPress、KeyDown、KeyUp、KeyCanChange是提供给应用程序使用的变量。
  KeyPress表示当前被按住不放的键;
  KeyDown表示新按下的键;
  KeyUp表示心松开的键;

  KeyCanChange是应用程序用来控制是否允许新的扫描。

  当某个按键被按下时,KeyPress对应位被置1,并且KeyDown对应位也置1;当按键松开后,KeyPress对应

位为0,KeyUp对应位被置1。

  3)定时器0中断处理

//函数功能:定时器0中断处理。

//22.1184M晶体约5ms中断一次。

voidTimer0Isr(void)interrupt1

{

//定时器0重装,定时间隔为5ms,加15是为了修正重装所花费时间

//这个值可以通过软件仿真来确定,在这里设置断点,调整使两次运行

//时间差刚好为5ms即可。

TH0=(65536-Fclk/1000/12*5+15)/256;

TL0=(65536-Fclk/1000/12*5+15)%256;


if(!KeyCanChange)return;//如果正在处理按键,则不再扫描键盘


//开始键盘扫描

//保存按键状态到当前按键情况

//KeyCurrent总共有8个bit

//当某个开关按下时,对应的bit为1

KeyCurrent=GetKeyValue();//读取键值,GetKeyValue()其实是个宏,不是函数,

//这里故意写成函数的样子,美观。它的定义在

//key.h文件中


if(KeyCurrent!=KeyOld)//如果两次值不等,说明按键情况发生了改变

{

KeyNoChangedTime=0;//键盘按下时间为0

KeyOld=KeyCurrent;//保存当前按键情况

return;//返回

}

else

{

KeyNoChangedTime++;	//按下时间累计

if(KeyNoChangedTime>=1)	//如果按下时间足够

{

KeyNoChangedTime=1;

KeyPress=KeyOld;//保存按键

KeyDown|=(~KeyLast)&(KeyPress);//求出新按下的键

KeyUp|=KeyLast&(~KeyPress);//求出新释放的键

KeyLast=KeyPress;	//保存当前按键情况

}

}

}



2.11.5串口驱动的编写


1.串口驱动的重要性:

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


2.usart.c

在使用串口之前,必须对串口进行初始化。

//函数功能:串口初始化

voidInitUART(void)

{

EA=0;    //暂时关闭中断

TMOD&=0x0F; //定时器1模式控制在高4位

TMOD|=0x20;//定时器1工作在模式2,自动重装模式

SCON=0x50;//串口工作在模式1

TH1=256-Fclk/(BitRate*12*16);//计算定时器重装值

TL1=256-Fclk/(BitRate*12*16);

PCON|=0x80;//串口波特率加倍

ES=1;//串行中断允许

TR1=1;//启动定时器1

REN=1;//允许接收

EA=1;//允许中断

}


串口中断处理函数

volatileuint8Sending;

//函数功能:串口中断处理。

voidUartISR(void)interrupt4

{

if(RI)//收到数据

{

RI=0;//清中断请求,因为这里只发送,不接受,故此处只要清除中断标志即可

}

else//发送完一字节数据

{

TI=0;

Sending=0;//清正在发送标志

}

}


发送单个字符函数

//函数功能:往串口发送一字节数据。

//入口参数:d:要发送的字节数据。

voidUartPutChar(uint8d)

{

SBUF=d;//将数据写入到串口缓冲

Sending=1;	//设置发送标志

while(Sending);//等待发送完毕

}


发送一个字符串函数

//函数功能:发送一个字符串。

//入口参数:pd:要发送的字符串指针。

voidPrints(uint8*pd)

{

while((*pd)!='\0')//发送字符串,直到遇到0才结束

{

UartPutChar(*pd);//发送一个字符

pd++;      //移动到下一个字符

}

}


以HEX格式发送一个整数

codeuint8HexTable[]={'0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'};

//函数功能:将短整数按十六进制发送。

//入口参数:待发送的整数。

voidPrintShortIntHex(uint16x)

{

uint8i;

uint8display_buffer[7];

display_buffer[6]=0;

display_buffer[0]='0';

display_buffer[1]='x';

for(i=5;i>=2;i--)//将整数转换为4个字节的HEX值

{

display_buffer[i]=HexTable[(x&0xf)];

x>>=4;

}

Prints(display_buffer);

}



2.11.6PDIUSBD12读写函数和读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设置为低即可(设置为数据状态)。

//函数功能:D12写命令。

//入口参数:Command:一字节命令。

voidD12WriteCommand(uint8Command)

{

D12SetCommandAddr();//设置为命令地址

D12ClrWr();      //WR置低

D12SetPortOut();   //将数据口设置为输出状态(注意这里为空宏,移植时可能有用)

D12SetData(Command); //输出命令到数据口上

D12SetWr();      //WR置高

D12SetPortIn();    //将数据口设置为输入状态,以备后面输入使用

}


  读一个字节的操作过程为:先将A0置低(即设置为数据状态),再将RD_N置低(表示读数据),读取P0口上的数据
并保存,最后将RD_N置高,结束读过程。最后函数返回读取到的数据。

//函数功能:读一字节D12数据。

//返回:读回的一字节。

uint8D12ReadByte(void)

{

uint8temp;

D12SetDataAddr();//设置为数据地址

D12ClrRd();    //RD置低

temp=D12GetData();//读回数据

D12SetRd();    //RD置高

returntemp;   //返回读到数据

}


  读取2字节的ID号

//函数功能:读D12的ID。

//返回:D12的ID。

uint16D12ReadID(void)

{

uint16id;

D12WriteCommand(Read_ID);    //写读ID命令

id=D12ReadByte();        //读回ID号低字节

id|=((uint16)D12ReadByte())<<8; //读回ID号高字节

returnid;

}


4.修改main函数

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


main.c

/********************************************************************

函数功能:主函数。

入口参数:无。

返回:无。

备注:无。

********************************************************************/

voidmain(void)//主函数

{

uint8i;

uint16id;


EA=1;//打开中断

InitKeyboard();//初始化按键

InitUART();//初始化串口


for(i=0;i<16;i++)	//显示信息

{

Prints(HeadTable[i]);

}


id=D12ReadID();


Prints("YourD12chip\'sIDis:");

PrintShortIntHex(id);


if(id==0x1012)

{

Prints(".IDiscorrect!Congratulations!\r\n\r\n");

}

else

{

Prints(".IDisincorrect!Whatapity!\r\n\r\n");

}


while(1)//死循环

{

	LEDs=~KeyPress;//将按键结果取反后控制LED

if(KeyDown)		//有键按下

{//处理按下的键

if(KeyDown&KEY1)

{

Prints("KEY1down\r\n");

KeyDown&=~KEY1;

}

if(KeyDown&KEY2)

{

Prints("KEY2down\r\n");

KeyDown&=~KEY2;

}

if(KeyDown&KEY3)

{

Prints("KEY3down\r\n");

KeyDown&=~KEY3;

}

if(KeyDown&KEY4)

{

Prints("KEY4down\r\n");

KeyDown&=~KEY4;

}

if(KeyDown&KEY5)

{

Prints("KEY5down\r\n");

KeyDown&=~KEY5;

}

if(KeyDown&KEY6)

{

Prints("KEY6down\r\n");

KeyDown&=~KEY6;

}

if(KeyDown&KEY7)

{

Prints("KEY7down\r\n");

KeyDown&=~KEY7;

}

if(KeyDown&KEY8)

{

Prints("KEY8down\r\n");

KeyDown&=~KEY8;

}

}


if(KeyUp)//有键释放

{//处理释放的键

if(KeyUp&KEY1)

{

Prints("KEY1up\r\n");

KeyUp&=~KEY1;

}

if(KeyUp&KEY2)

{

Prints("KEY2up\r\n");

KeyUp&=~KEY2;

}

if(KeyUp&KEY3)

{

Prints("KEY3up\r\n");

KeyUp&=~KEY3;

}

if(KeyUp&KEY4)

{

Prints("KEY4up\r\n");

KeyUp&=~KEY4;

}

if(KeyUp&KEY5)

{

Prints("KEY5up\r\n");

KeyUp&=~KEY5;

}

if(KeyUp&KEY6)

{

Prints("KEY6up\r\n");

KeyUp&=~KEY6;

}

if(KeyUp&KEY7)

{

Prints("KEY7up\r\n");

KeyUp&=~KEY7;

}

if(KeyUp&KEY8)

{

Prints("KEY8up\r\n");

KeyUp&=~KEY8;

}

}

}

}


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




2.12本章小结

  本章内容的必要性
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
相关文章推荐