2011年11月24日木曜日

ロータリスイッチを使う


by yosikawa » 2011年2月12日(土) 17:09
Spartan 3E Starter Kit 最初の一歩の続き

簡単この上ない「ボタンを押したらLEDが点灯する」アプリはちゃんと動いた。
次はロータリスイッチを使ってみる。
手で回してみると、「くりっ、くりっ」という、いい感じで回る。マウスのホイールと似た感じ。
数えてみると、20段階で1回転する。つまり、1段階について18度ずつ認識する。
しかも、プッシュスイッチ機能も付いていて、つまみを押しこむとスイッチとしてOn/OFFも認識できる。

新しいプロジェクトを作成し、トップモジュールを作成する。
コード: 
`timescale 1ns / 1ps
//////////////////////////////////////////////////////////////////////////////////
// Company: 
// Engineer: 
// 
// Create Date:    17:42:47 02/11/2011 
// Design Name: 
// Module Name:    top 
// Project Name: 
// Target Devices: 
// Tool versions: 
// Description: 
//
// Dependencies: 
//
// Revision: 
// Revision 0.01 - File Created
// Additional Comments: 
//
//////////////////////////////////////////////////////////////////////////////////
`default_nettype none
module top(clk, led, switch, btn, rotary_a, rotary_b, rotary_press);

   input wire clk;               // グローバルクロック
   input wire [3:0] switch;      // スライドスイッチ
   input wire [3:0] btn;         // プッシュスイッチ (3:north, 2:east, 1:west, 0:south)
   input wire rotary_a;            // ロータリスイッチA接点(アクティブ・ロー)
   input wire rotary_b;            // ロータリスイッチB接点(アクティブ・ロー)
   input wire rotary_press;      // ロータリスイッチ・プッシュ
   output wire [7:0] led;         // LED

   reg [1:0] reset_count;
   always @(posedge clk) begin
      if(&reset_count == 0)
         reset_count <= reset_count + 1;
   end
   wire reset = reset_count[0];   // リセット信号。
   
   wire rotary_event;            // ロータリスイッチが1段階回転したとき、1パルスだけアクティブになる。
   wire rotary_right;            // rotary_event がアクティブのとき、回転方向を示す。ハイが右方向。
   S3E_Rotary S3E_Rotary(clk, reset, ‾rotary_a, ‾rotary_b, rotary_event, rotary_right);

   reg [7:0] led1;
   wire [7:0] led2 = { btn, switch };
   wire [7:0] led3 = led1 ^ led2;
   
   always @(posedge clk or negedge reset) begin
      if(!reset) begin
         led1 <= 8'd1;
      end else if(rotary_event) begin
         if(rotary_right) begin
            led1 <= { led1[0], led1[7:1] };
         end else begin
            led1 <= { led1[6:0], led1[7] };
         end
      end
   end

   assign led = rotary_press ? ‾led3 : led3;

endmodule


S3E_Rotary はロータリスイッチを処理するモジュール。
ロータリスイッチのA接点とB接点はアクティブローなので、モジュールへの入力で反転している。

このモジュールは、XilinxのサイトにあるRotary Encoder Interfaceを参考に作成した。
http://www.xilinx.com/products/boards/s3estarter/reference_designs.htm
ただし、書いてある通りに作ると左右の回転が逆になる。A接点、B接点がアクティブローであることも考慮されてないようだ。
多分、この資料が作られたときから、ハードの仕様が変更されたのだろう。間違いなので、修正した。
S3E_Rotary.v を以下のようにして作成する。
コード: 
`timescale 1ns / 1ps
//////////////////////////////////////////////////////////////////////////////////
// Company: 
// Engineer: 
// 
// Create Date:    17:54:43 02/11/2011 
// Design Name: 
// Module Name:    S3E_Rotary 
// Project Name: 
// Target Devices: 
// Tool versions: 
// Description: 
//
// Dependencies: 
//
// Revision: 
// Revision 0.01 - File Created
// Additional Comments: 
//
//////////////////////////////////////////////////////////////////////////////////
`default_nettype none
module S3E_Rotary(clk, reset, rotary_a, rotary_b, rotary_event, rotary_right);

   input wire clk;               // グローバルクロック
   input wire reset;               // システムリセット
   input wire rotary_a;            // ロータリスイッチA接点
   input wire rotary_b;            // ロータリスイッチB接点
   output reg rotary_event;      // ロータリスイッチが1段階回転したとき、1パルスだけアクティブになる。
   output reg rotary_right;      // rotary_event がアクティブのとき、回転方向を示す。ハイが右方向。

   // rotary_q1;
   // Set (‘1’) when A is High and B is High
   // Reset (‘0’) when A is Low and B is Low.
   // Remember current state in all other cases.
   reg rotary_q1;
   // rotary_q2
   // Set (‘1’) when A is Low and B is High
   // Reset (‘0’) when A is High and B is Low.
   // Remember current state in all other cases.
   reg rotary_q2;
   
   always @(posedge clk or negedge reset) begin
      if(!reset) begin
         rotary_q1 <= 0;
         rotary_q2 <= 0;
      end else begin
         case ({ rotary_a, rotary_b })
            2'b00 : { rotary_q1, rotary_q2 } <= { 1'b0, rotary_q2 };
            2'b01 : { rotary_q1, rotary_q2 } <= { rotary_q1, 1'b1 };
            2'b10 : { rotary_q1, rotary_q2 } <= { rotary_q1, 1'b0 };
            2'b11 : { rotary_q1, rotary_q2 } <= { 1'b1, rotary_q2 };
            default: { rotary_q1, rotary_q2 } <= { rotary_q1, rotary_q2 };
         endcase
      end
   end
   
   // rotary_q1 のposedgeが、rotary_event になり、このときrotary_q2がrotary_rightになる。
   reg rotary_q1_w;
   always @(posedge clk or negedge reset) begin
      if(!reset) begin
         rotary_q1_w <= 0;
      end else begin
         rotary_q1_w <= rotary_q1;
      end
   end
   always @(posedge clk or negedge reset) begin
      if(!reset) begin
         rotary_event <= 0;
         rotary_right <= 0;
      end else if(rotary_q1 & !rotary_q1_w) begin
         rotary_event <= 1;
         rotary_right <= rotary_q2;
      end else begin
         rotary_event <= 0;
         rotary_right <= rotary_right;
      end
   end

endmodule


UCFファイルは、今回はいろんな接続が必要だ。以下のようにする。
コード: 
#
# Period constraint for 50MHz operation
#
NET "clk" PERIOD = 20.0ns HIGH 50%;
#
# soldered 50MHz Clock.

NET "clk" LOC = "C9" | IOSTANDARD = LVTTL;
#
#
# Simple LEDs 
# Require only 3.5mA. 
#
NET "led<0>" LOC = "F12" | IOSTANDARD = LVTTL | SLEW = SLOW | DRIVE = 4;
NET "led<1>" LOC = "E12" | IOSTANDARD = LVTTL | SLEW = SLOW | DRIVE = 4;
NET "led<2>" LOC = "E11" | IOSTANDARD = LVTTL | SLEW = SLOW | DRIVE = 4;
NET "led<3>" LOC = "F11" | IOSTANDARD = LVTTL | SLEW = SLOW | DRIVE = 4;
NET "led<4>" LOC = "C11" | IOSTANDARD = LVTTL | SLEW = SLOW | DRIVE = 4;
NET "led<5>" LOC = "D11" | IOSTANDARD = LVTTL | SLEW = SLOW | DRIVE = 4;
NET "led<6>" LOC = "E9"  | IOSTANDARD = LVTTL | SLEW = SLOW | DRIVE = 4;
NET "led<7>" LOC = "F9"  | IOSTANDARD = LVTTL | SLEW = SLOW | DRIVE = 4;
#
# Simple switches 
#   Pull UP resistors used to stop floating condition during switching. 
#
NET "switch<0>" LOC = "L13" | IOSTANDARD = LVTTL | PULLUP;
NET "switch<1>" LOC = "L14" | IOSTANDARD = LVTTL | PULLUP;
NET "switch<2>" LOC = "H18" | IOSTANDARD = LVTTL | PULLUP;
NET "switch<3>" LOC = "N17" | IOSTANDARD = LVTTL | PULLUP;
#
#
# Press buttons 
#   Must have pull DOWN resistors to provide Low when not pressed.
#
# btn_north
NET "btn<3>" LOC = "V4"  | IOSTANDARD = LVTTL | PULLDOWN;
# btn_west
NET "btn<2>"  LOC = "D18" | IOSTANDARD = LVTTL | PULLDOWN;
# btn_south
NET "btn<1>" LOC = "K17" | IOSTANDARD = LVTTL | PULLDOWN;
# btn_east
NET "btn<0>"  LOC = "H13" | IOSTANDARD = LVTTL | PULLDOWN;
#
# Rotary encoder. 
#   Rotation contacts require pull UP resistors to provide High level.
#   Press contact requires pull DOWN resistor to provide Low when not pressed..
#
NET "rotary_a"     LOC = "K18" | IOSTANDARD = LVTTL | PULLUP;
NET "rotary_b"     LOC = "G18" | IOSTANDARD = LVTTL | PULLUP;
NET "rotary_press" LOC = "V16" | IOSTANDARD = LVTTL | PULLDOWN;
#
#
# End of File
#


完成。
今回作ったのは、最初に右端のLEDが1個だけ点灯し、
ロータリスイッチを回すと左右に動く。
8個の各スイッチを押すと対応するLEDが点灯。
ロータリスイッチを押しこむとLED全体が反転する。

0 件のコメント:

コメントを投稿