irda红外收发器应用
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
《FPGA原理及应用》
课程设计
一、课程设计目的
本文设计实现一个基于FPGA的红外遥控解码和实现FPGA与PC机的串口通信。
二、系统总体框架
本系统综合FPGA和PC平台,总体功能框架如图2-1所示:
图2-1:系统功能组成图
系统主要包括四大模块:每个模块对应的作用分别如下:
1)红外遥控器解码模块:
主要是通过红外接收头接收红外遥控器发送的红外波形,通过FPGA对其进行解调和解码,得到遥控器每个按键对应的命令码。
2)FPGA串口发送模块:
FPGA解码获得遥控器每个按键对应的命令码后,需要将其发送到PC机上,以控制PC机上的多媒体播放器。
3)PC串口接收模块:
PC端需要控制串口,以获取串口传入的数据,也即是遥控器对应的命令码。
得到该码后,需要将其映射到PC机系统的按键事件,以控制PC机上的软件(这里是用XBMC多媒体播放器来演示)。
4)多媒体演示模块:
采用现有的XBMC多媒体中心来实现。
XBMC是一个跨平台的媒体娱乐中心软件(/download/),它可以播放音视频、浏览图片、查看天气、管理存储器上的媒体资源文件、系统设置、支持游戏手柄等功能下面对每一个模块的设计细节进行详细的介绍。
三、关键模块设计细节
3.1红外遥控器解码模块
本模块的功能主要是通过红外接收头接收红外遥控器发送的红外波形,通过FPGA对其进行解调和解码,得到遥控器每个按键对应的命令码。
1)红外遥控基本知识
通用红外遥控系统由发射和接收两大部分组成。
应用编/解码专用集成电路芯片来进行控制操作,如图3-1所示。
发射部分包括键盘矩阵、编码调节(增强发射效率)、LED红外发送器;接收部分包括光、电转换放大器、解调、解码电路。
图3-1:通用红外遥控系统
遥控发射器专用芯片很多,也存在很多编码协议。
NanoBoard2的遥控器采用的是广泛的NEC协议,这里简述下该协议。
该协议采用脉宽调制的串行码,以脉宽为0.565ms、间隔0.56ms、周期为1.125ms的组合表示二进制的“0”;以脉宽为0.565ms、间隔1.685ms、周期为2.25ms 的组合表示二进制的“1”,其波形如图3-2所示:
图3-2:0与1的波形
当我们按下遥控器的按键时,遥控器将发出一串二进制代码,我们称它为一帧数据。
可将它们分为5 部分,分别为引导码、用户码、用户反码、数据码和数据反码,共32bit。
如图3-3所示:
图3-3:一帧红外数据
遥控器发射代码时,均是低位在前。
高位在后。
其中引导码高电平为9ms,低电平为4.5ms,当接收到此码时,表示一帧数据的开始。
解码的关键是如何识别"0"和"1",从位的定义我们可以发现"0"和"1"均以0.565ms的低电平开始,不同的是高电平的宽度不同,"0"为0.56ms,"1"为1.68ms,所以必须根据高电平的宽度区别"0"和"1"。
2)本系统遥控解码
NanoBoard2的遥控器采用的是广泛的NEC协议。
在NanoBoard2的外设板PB03提供了红外收发器(infrared transceiver)。
在Altium Designer的库中也提供了该红外收发器的编程控制接口(FPGA PB03 Port-Plugin.IntLib中)。
如图3-4所示。
图3-4:NB2板上红外资源
但该收发器采用的是Vishay半导体公司的 TFDU6102收发芯片,其中并不像
一般的红外接收模块一样,这款芯片不提供红外解调功能。
所以我们通过Altium Designer提供的Wishbone的红外解调解码IP核来实现解调和解码功能。
该IP 核在Altium Designer的FPGA Peripherals (Wishbone).IntLib中,其引脚如图3-5所示:
图3-5:WB_IRDEC引脚图
各引脚的功能简述如下:
左边的部分是红外的控制信号。
右边的部分是遵守Wishbone规范的从设备接口,具体说明如下:
CLK_I:外部输入时钟信号。
RST_I:同步复位信号,高电平有效。
DAT_O()/DAT_I():主设备和从设备的之间的数据信号,数据可以由主设备传送给从设备,也可以由从设备传送给主设备。
一对主设备和从设备之间最多存在两条数据总线,一条用于主设备向从设备传输数据,另外一条用于从设备向主设备传输数据。
ADR_I[3:0]:地址信号,主设备输出地址到从设备。
ACK_O:主从设备间的操作结束方式信号。
ACK为高表示成功。
操作总是在某一总线周期内完成的,因此操作结束方式也称为总线周期结束方式。
CYC_I:总线周期信号CYC_I有效代表一个主设备请求总线使用权或者正在占有总线,但是不一定正在进行总线操作(是否正在进行总线操作取决于选通信号STB_I是否有效)。
只有该信号有效,Wishbone主设备和从设备接口的其它信号才有意义。
STB_I:选通信号。
选通有效代表主设备发起一次总线操作。
WE_I:写使能信号,代表当前周期中进行的操作是写操作还是读操作。
1代表写,0代表读。
这样我们就可以通过FPGA去控制WB_IRDEC来获得解码后的32bit遥控码。
WB_IRDEC提供了四个寄存器:Clock Divider Register (CLK_DIV)、Control Register (CTRL)、Status Register (STATUS)和Data Register (DATA)。
我们需要读写这四个寄存器来配置WB_IRDEC和读取红外码。
WB_IRDEC采用的是标准的Wishbone数据传输握手协议。
我们将该协议总结如下:
a)单次读操作:
在时钟上升沿i,主设备将地址信号ADR_O()放到总线上,将WE_O置为低表示读操作,将CYC_O置高表示操作正在进行,将STB_O置高表示操作开始。
在时钟上升沿i+1到达之前,从设备检测到主设备发起的操作,将适当的数据放到主设备的输入信号DAT_I(),将主设备的ACK_I置高作为对主设备STB_O 的响应。
在时钟上升沿i+1,主设备发现ACK_I信号为高,将DAT_I()采样,并将STB_O 和CYC_O置为低表示操作完成。
从设备发现STB_O置低后,也将主设备的输入信号ACK_I置低。
b)单次写操作:
在时钟上升沿i,主设备将地址信号ADR_O()放到总线上,将数据信号DAT_O()放到总线上,将WE_O置高表示写操作,将CYC_O置高表示操作正在进行,将STB_O 置高表示操作开始。
在时钟上升沿i+1到达之前,从设备检测到主设备发起的操作,将主设备的ACK_I置高作为对主设备STB_O的响应。
在时钟上升沿i+1,从设备将DAT_I()采样;主设备发现ACK_I信号为高,将STB_O和CYC_O置为低表示操作完成;从设备发现STB_O置低后,也将主设备的ACK_I置低。
具体的寄存器配置和读写参考altium提供的wiki:WB_IRRC - Accessible
Internal Registers。
另外,该说明文档里面时钟分频寄存器Clock Divider Register (CLK_DIV)的设置值的公式存在错误。
因为该寄存器的值提供解调红外波形的载波频率。
所以这个值的设置必须准确,否则无法解调。
所以这个错误使得调试过程中耗费了很长时间。
后来通过对原理的理解,自己推导到了正确的公式。
该IP核采用的时钟分频是通过24bit相位累加器来实现的。
它在wiki上提供的确定分频该寄存器的值的公式是:
CLK_DIV = (fcarrier * 8000000h) / fCLK_I
这里的8000000h应该是1000000h(24bit也就是224=1000000h)才正确。
我们设计的原理图如图3-6所示:
图3-6:红外解码模块原理图
我们的FPGA控制模块在IrDA.v文件中实现。
我们采用的是verilog硬件描述语言。
具体的verilog代码见附录。
3.2FPGA串口发送模块
1)串口基础知识
串口通信是指使用一条数据线(另外需要地线,可能还需要控制线),将数据一位一位地依次传输,每一位数据占据一个固定的时间长度。
其只需要少数几条线就可以在系统间交换信息,特别使用于计算机与计算机、计算机与外设之间的远距离通信。
使用串口通信时,发送和接收到的每一个字符实际上都是一次一位的传送的,每一位为1或者为0。
如图3-7所示。
图3-7:串行通信原理
串行异步通信即RS232通信,是主机与外部硬件设备的常用通讯方式。
可以双向传输。
其数据格式如图3-8所示。
图3-8:异步通信的数据格式
2)本系统串行通信发送模块设计
NanoBoard2的主板提供了RS232的串行接口。
在Altium Designer的库中也提供了该接口的编程控制接口(FPGA NB2DSK01 Port-Plugin.IntLib中)。
如图3-9所示:
图3-9:板上RS232的串行接口
在verilog代码中,我们需要根据我们选择的串口的波特率来对输入时钟进行分频得到串行通信的每一个数据位所需要的时间。
然后按照图3-8所示的串行通信的数据格式将8bit的数据发送出去。
闲时我们将串口发射的信号线
UART_TX_O一直保持在高。
当我们需要发送数据时,将其拉低并保持一个数据位的时间,再将8bit的数据从低位到高位一位一位的通过发送出去。
然后再发送一
个停止位。
这里我们不添加校验位。
这时候一个8bit的数据就成功通过串口发送出去了。
我们设计的原理图如图3-10所示:
图3-10:串口发送模块原理图
我们的FPGA控制模块在UART_TX.v文件中实现。
然后在IrDA.v文件中实例化。
这样就把串口发送模块嵌入到红外解码模块中。
具体的verilog代码见附录。
3.3PC串口接收和键盘映射模块
PC端需要控制串口,以获取串口传入的数据,也即是遥控器对应的命令码。
得到该码后,需要将其映射到PC机系统的按键事件,以控制PC机上的软件(这里是用XBMC多媒体播放器来演示)。
1)串口接收
在Windows系统中,串口和其它通信设备是作为文件处理的。
串口的打开、关闭、读取和写入所用的函数与操作文件的函数完全一致。
我们通过CreateFile()函数“打开”串口。
函数创建或打开以下对象并返回一个句柄,可以用来访问对象:文件、管道、mailslots、通信资源、磁盘设备(Windows NT只有)、consoles等。
HANDLE CreateFile(
LPCTSTR lpFileName, //指向文件名的指针
DWORD dwDesiredAccess, //访问模式(写/读)
DWORD dwShareMode, //共享模式
LPSECURITY_ATTRIBUTES lpSecurityAttributes, //指向安全属性的指针DWORD dwCreationDisposition, //如何创建
DWORD dwFlagsAndAttributes, //文件属性
HANDLE hTemplateFile //用于复制文件句柄
);
打开串口之后,我们再通过ReadFile()函数从文件指针指向的位置开始将数据读出到来。
BOOL ReadFile(
HANDLE hFile, //文件的句柄
LPVOID lpBuffer, //用于保存读入数据的一个缓冲区
DWORD nNumberOfBytesToRead, //要读入的字节数
LPDWORD lpNumberOfBytesRead, //指向实际读取字节数的指针LPOVERLAPPED lpOverlapped //该结构定义了一次异步读取操作
);
2)键盘映射模块
通过串口获得每个遥控按键对应的8bit,也就是一个字节的码后。
我们需要将其与遥控器具体的按键功能对应。
例如Altium的遥控器的“向左”这个按键的红外指令码是0x47。
当我们接收到这个码后,我们就映射到PC机键盘上面的“向左”的键盘值。
我们用Windows提供了Keybd_event这个函数来模拟键盘。
通过调用它来产生“向左”这个按键的键值。
这个键值会发送到系统上,然后当前的活动程序将会捕捉到这个按键,然后达到控制软件的目的。
Keybd_event能触发一个按键事件,Keybd_event共有四个参数,第一个为按键的虚拟键值,如回车键为vk_return。
第二个参数为扫描码,一般不用设置,用0代替就行。
第三个参数为选项标志,如果为keydown则置0即可,如果为keyup 则设成“KEYEVENTF_KEYUP”,第四个参数一般也是置0即可。
下面两行代码就实
现了模拟键盘“向左”的按键按下然后放开的效果:
keybd_event(VK_LEFT, 0, 0, 0); // key down
keybd_event(VK_LEFT, 0, KEYEVENTF_KEYUP,0); // key up
具体源码见附录。
3.4多媒体演示模块
该模块的作用是演示我们对遥控器解码的有效性。
采用现有的XBMC多媒体中心来实现。
XBMC是一个跨平台的媒体娱乐中心软件,它可以播放音视频、浏览图片、查看天气、管理存储器上的媒体资源文件、系统设置、支持游戏手柄等功能。
其效果图见图3-11。
图3-11:XBMC多媒体软件
四、主要代码
4.1FPGA端
IrDA_v.v
1.//===========================================================================
===
2.// File Name : IrDA_v.v
3.// Module Name : This is IrDA control module
4.// Description : Receive the IrDA code, then decode it and transmit the code
to PC though COM
5.// Author : Zou Xiaoyi
6.// HomePage : /zouxy09
7.// Date : 2013/06/08
8.// Rev. : 0.1
9.//===========================================================================
===
10.
11.module IrDA_WB(CLK_I, ACK_I, INT_I, DATA_I, reset, CYC_STB_O, WE_O, ADR_O, DA
TA_O, UART_TX_O, Ir_Commad);
12. input CLK_I;
13. input ACK_I;
14. input INT_I;
15. input [31:0] DATA_I;
16. input reset;
17.
18. output CYC_STB_O;
19. output WE_O;
20. output [3:0] ADR_O;
21. output [31:0] DATA_O;
22. output UART_TX_O;
23. output [7:0] Ir_Commad;
24.
25. reg ACK_I;
26. reg INT_I;
27. reg [31:0] DATA_I;
28.
29. reg CYC_STB_O;
30. reg WE_O;
31. reg [3:0] ADR_O;
32. reg [31:0] DATA_O;
33. reg [7:0] Ir_Commad;
34. reg [7:0] data;
35.
36. reg [31:0] count;
37. reg [31:0] count2;
38. reg flag;
39.
40. // The whole process of IrDA decoder
41. always @(posedge CLK_I)
42. begin
43. // count posedge of clk
44. if (reset) // initialize
45. begin
46. count = 32'd0;
47. count2 = 32'd0;
48. flag = 1'b0;
49. CYC_STB_O = 1'b0;
50. START = 1'b0;
51. end
52.
53. // 1) initialize
54. // /display/ADOH/WB_IRRC+-+Accessible+Inter
nal+Registers
55. case (count)
56. 32'd0:
57. begin
58. Ir_Commad = 8'b0000_1111; // initialize state
59. count = 32'd1;
60. end
61.
62. 32'd1:
63. begin
64. // write Clock Divider Register (CLK_DIV) Register
65. ADR_O = 4'h0;
66. DATA_O[31:0] = 32'h0000_7D3E; // 32'h0000_3219 for 50M
Hz 32'h0000_7D3E for 20MHz
67. WE_O = 1'b1;
68. CYC_STB_O = 1'b1;
69. count = 32'd2;
70. end
71.
72. 32'd2:
73. begin
74. if (ACK_I) // waiting for ack
75. begin
76. CYC_STB_O = 1'b0;
77. count = 32'd3;
78. end
79. else
80. count = 32'd2;
81. end
82.
83. 32'd3:
84. begin
85. // write Control Register
86. ADR_O = 4'h1;
87. DATA_O[31:0] = 32'h0000_0043; // 43
88. WE_O = 1'b1;
89. CYC_STB_O = 1'b1;
90. count = 32'd4;
91. end
92.
93. 32'd4:
94. begin
95. if (ACK_I) // waiting for ack
96. begin
97. CYC_STB_O = 1'b0;
98. count = 32'd5;
99. count2 = 32'd0;
100. end
101. else
102. count = 32'd4;
103. end
104.
105. default:
106. begin
107. // make initialize strage run once
108. count = 32'd10;
109.
110. // 2) if it has valid data, get it!
111. if (INT_I) // haveData
112. begin
113. // 2.1) we need to read it from data register 114. case (count2)
115. 32'd0:
116. begin
117. // read Data Register
118. ADR_O = 4'h3;
119. WE_O = 1'b0;
120. CYC_STB_O = 1'b1;
121. count2 = 32'd1;
122. end
123.
124. 32'd1:
125. begin
126. if (ACK_I) // waiting for ack
127. begin
128. flag = 1'b1;
129. data = DATA_I[23:16];
130. CYC_STB_O = 1'b0;
131. count2 = 32'd2;
132. //START = 1'b1; // start to tr ansmit though COM
133. end
134. else
135. count2 = 32'd1;
136. end
137.
138. 32'd2:
139. begin
140. // 2.2) write Control Register to clear bit of data flag
141. ADR_O = 4'h1;
142. DATA_O[31:0] = 32'h0000_0043; //43 143. WE_O = 1'b1;
144. CYC_STB_O = 1'b1;
145. count2 = 32'd3;
146. end
147.
148. 32'd3:
149. begin
150. if (ACK_I) // waiting for ack
151. begin
152. CYC_STB_O = 1'b0;
153. count2 = 32'd0;
154. START = 1'b1; // start to trans mit though COM
155. end
156. else
157. count2 = 32'd3;
158. end
159.
160. default:
161. begin
162. count2 = 32'd0;
163. end
164.
165. endcase
166. end
167. else
168. begin
169. START = 1'b0; // no data to transmit
170. if (flag)
171. Ir_Commad = data; //data;
172. else
173. Ir_Commad = 8'b1111_0000; // No data 174. end
175. end
176. endcase
177. end
178.
179.
180. // The whole process of COM transmit module
181. wire RST_B;
182. reg START;
183. wire TX_BUSY;
184.
185. assign RST_B = ~reset;
186.
187.
188. //Instance DUT
189. UART_TX I_UART_TX
190. (
191. .SYSCLK (CLK_I ),
192. .RST_B (RST_B ),
193.
194. .START (START ),
195. .UART_TX_DATA (data ),
196. .UART_TX_O (UART_TX_O ),
197. .TX_BUSY (TX_BUSY )
198. );
199.
200.endmodule
UART_TX.v
1.//===========================================================================
===
2.// File Name : UART_CTL.v
3.// Module Name : This is UART control module
4.// Description :
5.// Author : Zou Xiaoyi
6.// HomePage : /zouxy09
7.// Date : 2013/06/08
8.// Rev. : 0.1
9.//===========================================================================
===
10.
11.`define UD #1
12.
13.module UART_TX
14. (
15. SYSCLK,
16. RST_B,
17.
18. START,
19. UART_TX_DATA,
20. UART_TX_O,
21.
22. TX_BUSY
23. );
24.//===========================================================================
===
25.// Input and output deceleration
26.//===========================================================================
===
27.input SYSCLK; //系统时钟50MHz
28.input RST_B; //全局复位信号
29.
30.input START; //由主控层发来的启动发送的脉冲
31.input [7:0] UART_TX_DATA; //主控层传来的需要发送的数据
32.output UART_TX_O; //串口的串行输出数据线,TX
33.
34.output TX_BUSY; //控制层返回给主控层的"忙信号",1 表示正在发送中
35.
36.//===========================================================================
===
37.// Wire and reg deceleration
38.//===========================================================================
===
39.wire SYSCLK;
40.wire RST_B;
41.
42.wire START;
43.wire [7:0] UART_TX_DATA;
44.reg UART_TX_O;
45.
46.reg TX_BUSY;
47.
48.//===========================================================================
===
49.// Wire and reg in the module
50.//===========================================================================
===
51.reg [11:0] TIME_CNT; //系统时钟计数器,根据波特率计算每一位的时间
52.reg [11:0] TIME_CNT_N; //TIME_CNT 的下一个状态
53.
54.reg [3:0] BIT_CNT; //位计数器,在状态机中用来控制每个状态停留的时间
55.reg [3:0] BIT_CNT_N; //BIT_CNT 的下一个状态
56.
57.reg [9:0] SHIFT_DATA; //输出移位寄存器,加上起始、停止位共10位
58.reg [9:0] SHIFT_DATA_N; //SHIFT_DATA 的下一个状态
59.
60.reg [2:0] UART_TX_CS; //发送状态机的当前状态
61.reg [2:0] UART_TX_NS; //发送状态机的下一下状态
62.
63.reg UART_TX_O_N; //UART_TX_O 的下一个状态
64.
65.reg TX_BUSY_N; //TX_BUSY 的下一个状态
66.reg [1:0] START_REG; //记录发送脉冲的边沿变化
67.
68.//---------------------------------------------------------------------------
---
69.
70.parameter IDLE = 3'h0; //状态机空闲状态
71.parameter SEND_START = 3'h1; //状态机发送开始码的状态
72.parameter SEND_DATA = 3'h2; //状态机发送8位数据的状态
73.parameter SEND_STOP = 3'h3; //状态机发送停止位的状态
74.parameter FINISH = 3'h4; //状态机的结束状态
75.
76.//---------------------------------------------------------------------------
---
77.always @ (posedge SYSCLK or negedge RST_B)
78. begin
79. if(!RST_B)
80. START_REG <= `UD 2'h0;
81. else
82. START_REG <= `UD {START_REG[0], START};
83. end
84.
85.//---------------------------------------------------------------------------
---
86.always @ (posedge SYSCLK or negedge RST_B)
87. begin
88. if(!RST_B)
89. TX_BUSY <= `UD 1'h0;//0 -> IDLE ,1->BUSY
90. else
91. TX_BUSY <= `UD TX_BUSY_N;
92. end
93.
94.//BUSY 信号为忙时,是状态机不为 IDLE 的所有状态
95.always @ (*)
96. begin
97. if(UART_TX_CS == IDLE)
98. TX_BUSY_N = 1'h0;
99. else
100. TX_BUSY_N = 1'h1;
101. end
102.
103.//------------------------------------------------------------------------------
104.
105.always @ (posedge SYSCLK or negedge RST_B)
106. begin
107. if(!RST_B)
108. TIME_CNT <= `UD 12'h0;
109. else
110. TIME_CNT <= `UD TIME_CNT_N;
111. end
112.
113.
114.//parameter COUNT = 9'h1B2; // for 50MHz and 115200
115.//parameter COUNT = 9'h1b2; // for 50MHz and 9600
116.//parameter COUNT = 9'hAE; // for 20MHz and 115200
117.parameter COUNT = 12'h820; // for 20MHz and 9600
118.
119.// 波特率率为115200 ,每一位的周期是8.68us, 计数值为,9'h1b2
120.// 这里计数范围为 0 ~ 9'h1b1
121.always @ (*)
122. begin
123. if(TIME_CNT == COUNT)
124. TIME_CNT_N = 12'h0;
125. //不为IDLE时才会发数据,也才需要计数器计数
126. else if(UART_TX_CS != IDLE)
127. TIME_CNT_N = TIME_CNT + 12'h1;
128. else
129. TIME_CNT_N = TIME_CNT;
130. end
131.//------------------------------------------------------------------------------
133. begin
134. if(!RST_B)
135. SHIFT_DATA <= `UD 10'h0;
136. else
137. SHIFT_DATA <= `UD SHIFT_DATA_N;
138. end
139.
140.always @ (*)
141. begin
142. //在发数据状态的第一时刻把要发送的10位数据先加载进来。
143. if((TIME_CNT == 12'h0) && (UART_TX_CS == SEND_START))
144. SHIFT_DATA_N = {1'b1,UART_TX_DATA[7:0],1'b0};
145. //TIME_CNT 每一次为0时,就需要移出一位数据到TX线上
146. else if(TIME_CNT == 12'h0)
147. SHIFT_DATA_N = {1'b1,SHIFT_DATA[9:1]};
148. else
149. SHIFT_DATA_N = SHIFT_DATA;
150. end
151.
152.//------------------------------------------------------------------------------
153.always @ (posedge SYSCLK or negedge RST_B)
154. begin
155. if(!RST_B)
156. BIT_CNT <= `UD 4'h0;
157. else
158. BIT_CNT <= `UD BIT_CNT_N;
159. end
160.
161.always @ (*)
162. begin
163. //每次状态机状态发生变化时,计数器重新清0,为下一次从0开始计数做准备
164. if(UART_TX_CS != UART_TX_NS)
165. BIT_CNT_N = 4'h0;
166. //BIT_CNT 是对TIME_CNT的计数周期进行计数
167. else if(TIME_CNT == COUNT)
168. BIT_CNT_N = BIT_CNT + 4'h1;
169. else
170. BIT_CNT_N = BIT_CNT;
171. end
172.
173.//------------------------------------------------------------------------------
175. begin
176. if(!RST_B)
177. UART_TX_O = 1'b1;
178. else
179. UART_TX_O = UART_TX_O_N;
180. end
181.
182.//TX信号在发送过程中始终等于移位寄存器的0位,TX是低位先发,其它状态时保持高电平183.always @ (*)
184. begin
185. if((UART_TX_CS == IDLE) || (UART_TX_CS == FINISH))
186. UART_TX_O_N = 1'b1;
187. else
188. UART_TX_O_N = SHIFT_DATA[0];
189. end
190.
191.//------------------------------------------------------------------------------
192.// 发送流程控制的核心状态机
193.always @ (posedge SYSCLK or negedge RST_B)
194.begin
195. if(!RST_B)
196. UART_TX_CS <= `UD IDLE;
197. else
198. UART_TX_CS <= `UD UART_TX_NS;
199.end
200.
201.always @ (*)
202.begin
203. case(UART_TX_CS)
204.
205. IDLE :
206. if(START_REG)
207. UART_TX_NS = SEND_START;
208. else
209. UART_TX_NS = UART_TX_CS;
210.
211. SEND_START :
212. //发送状态必须保持完整的计数周期,每一位的时间严格保证
213. if((BIT_CNT == 4'h0) && (TIME_CNT == COUNT))
214. UART_TX_NS = SEND_DATA;
215. else
216. UART_TX_NS = UART_TX_CS;
217.
218. SEND_DATA :
219. if((BIT_CNT == 4'h7) && (TIME_CNT == COUNT))
220. UART_TX_NS = SEND_STOP;
221. else
222. UART_TX_NS = UART_TX_CS;
223.
224. SEND_STOP :
225. if((BIT_CNT == 4'h0) && (TIME_CNT == COUNT))
226. UART_TX_NS = FINISH;
227. else
228. UART_TX_NS = UART_TX_CS;
229. FINISH :
230. if((BIT_CNT == 4'h0) && (TIME_CNT == COUNT))
231. UART_TX_NS = IDLE;
232. else
233. UART_TX_NS = UART_TX_CS;
234. default :
235. UART_TX_NS = IDLE;
236. endcase
237.end
238.endmodule
239.
240.//========================================================================== ====
241.// End of file
242.//========================================================================== ====
4.2PC端
Main.cpp
1.// Description : Receive IR command code from UART
2.// Author : Zou Xiaoyi
3.// HomePage : /zouxy09
4.// Date : 2013/06/08
5.// Rev. : 0.1
6.
7.#include "serialPort.h"
8.#include <iostream>
9.#include <windows.h>
10.#include <stdlib.h>
11.
ing namespace std;
13.
14.#define NUM_OF_KEY 7
15.
16.enum COMMAND
17.{
18. STOP,
19. ENTER,
20. LEFT,
21. RIGHT,
22. UP,
23. DOWN,
24. STANDBY
25.};
26.
27.char command[NUM_OF_KEY] = {0x52, 0x07, 0x47, 0x40, 0x06, 0x44, 0x14 };
28.char *command_name[NUM_OF_KEY] =
29.{
30."STOP",
31."ENTER",
32."LEFT",
33."RIGHT",
34."UP",
35."DOWN",
36."STANDBY"
37.};
38.
39.char command2key[NUM_OF_KEY] = {VK_CANCEL, VK_RETURN, VK_LEFT, VK_RIGHT, VK_U
P, VK_DOWN, VK_ESCAPE};
40.
41.int main()
42.{
43. SerialPort serial(2, 9600);
44.if (!serial.Port_Init())
45. {
46. cout<<"The COM is not open error!"<<endl;
47.return -1;
48. }
49.
50.HANDLE comHandle = serial.getHANDLE();
51.
52. cout<<"Waiting for IR remote controler to send command..."<<endl;
53.
54.while(1)
55. {
56.char buf[10];
57.
58.int length = serial.readData(buf[0], 10);
59.
60.if (length)
61. {
62.for (int i = 0; i < NUM_OF_KEY; i++)
63. {
64.if (command[i] == buf[0])
65. {
66. cout<<"The key you press is: "<<command_name[i]<<endl;
67.
68. keybd_event(command2key[i], 0, 0, 0); // ke
y down
69. keybd_event(command2key[i], 0, KEYEVENTF_KEYUP,0); // ke
y up
70.
71.break;
72. }
73.else if (i == (NUM_OF_KEY - 1))
74. {
75. cout<<"This is a new key, you can add to our program for
regconizing it next time"<<endl;
76. }
77. }
78. }
79. }
80.
81. serial.Port_Close();
82.return 0;
83.}
五、总结
为期一周的FPGA课程设计结束了,通过这次课设,让我对Modelsim仿真的使用更加熟练。
这次课设看似功能简单,但它包含了FPGA设计中最核心、最重要的问题,很有深度。
今后学习还是要扎实点,把简单的东西在深度上多挖掘,只要基础打好了,开发大的项目就不会在犯低级错误了。