LoginSignup
3
6

More than 5 years have passed since last update.

Atlas-SoCで自作ハードを試す PWM編

Posted at

やること

前回、1からHPSのプロジェクトを作って見ました。LEDをつけるだけだと普通のCPUでも簡単にできるのでハードっぽいのを見たいところです。
今回は秋月のサーボモータを動かしてみます。
M-08761.jpg

仕様確認

秋月のHPからデータシートを確認できます。
3.png
全体をperiod,ONの時間をdutyと呼ぶみたいです。

FPGAのコード変更

PWMの処理ですが、まずはLEDをPWMでチカチカさせてみます。8つあるけど一緒にチカチカ。C言語で書くとこんな感じでしょうかね。

while(1)
{
    for(count=0; count<period; count++)
        led = (count<duty);
}

前回のプロジェクトをそのまま使います。ファイル名を変えると面倒くさいのでファイル名はそのまま処理だけ変えることにします。
led.vを以下のように修正しました。

led.v
// led.v
// 簡単なPWM回路

`timescale 1 ps / 1 ps
module new_component (
        input  wire [7:0]   avs_s0_address,     // avs_s0.address
        input  wire         avs_s0_read,        //       .read
        output reg  [31:0]  avs_s0_readdata,    //       .readdata
        input  wire         avs_s0_write,       //       .write
        input  wire [31:0]  avs_s0_writedata,   //       .writedata
        input  wire         clock_clk,          //  clock.clk
        input  wire         reset_reset,        //  reset.reset
        output wire [7:0]   led                 //  led output
    );

    reg [31:0] count;
    reg [31:0] duty;
    reg [31:0] period;

    // レジスタ読み込み 
    always @(posedge clock_clk)
    begin
        case(avs_s0_address)
            8'h00   : avs_s0_readdata <= period;
            8'h01   : avs_s0_readdata <= duty;
            8'h02   : avs_s0_readdata <= count;
            default : avs_s0_readdata <= 0;
        endcase
    end

    // レジスタ書き込み
    always @(posedge clock_clk)
    begin
        if(reset_reset)
        begin
            duty  <= 0;
            period <= 0;
        end
        else if(avs_s0_write)
        begin
            case(avs_s0_address)
                8'h00   : period <= avs_s0_writedata;
                8'h01   : duty   <= avs_s0_writedata;
            endcase
        end
    end

    // PWMカウンタ
    always @(posedge clock_clk)
    begin
        if(reset_reset)
            count <= 0;
        else if(count>=period)
            count <= 0;
        else
            count <= count+1;
    end

    // とりあえずLED全点灯してみる
    assign led = (count<=duty) ? 8'hff : 8'h00;

endmodule

CPUから見えるアドレスは32bit換算で
0000 : period
0001 : duty
としました。
このファイルだけ変えてQuartusでコンパイルし直します。

動作確認

出来上がったATLAS_SOC_GHRD.rbfをここで作った環境の
fat/ATLAS_SOC_GHRD/output_files/ATLAS_SOC_GHRD.rbf
にコピーします。
再起動します。
reboot
再起動すると新しいFPGAになっています。

最初に1秒周期、0.5秒ONというのを試してみます。
led.vに入ってくるクロックはHPSの設定で100MHzになっています。
5.png
1秒は100,000,000clkなので、電卓アプリで100,000,000を16進数に変換すると0x5f5e100。0.5秒はその半分で0x2faf080と計算できました。
cd app
./lw 0 5f5e100
./lw 1 2faf080
と入力するとLEDが1秒間隔で点滅します。

サーボモーターをつなげる

Atlas-SoC基板の上のピンヘッダGPIO0にサーボモータをつなげることにします。Atlas-SoCの回路図からGPIO0のピンアサインは
6.png
サーボモータは5Vなので電源は
pin11:Vcc 5V
pin12:GND
を使うことにします。
制御出力はその近くのGPIOを使うことにします。
pin10:GPIO_0_D9
サーボモータのピンアサインは以下になっていたので
2.png
オレンジ色コンタクトをハウジングから抜いて、茶色と赤をそのまま11,12に挿します。オレンジ色はそのまま10ピンにつなげます。
4.JPG

TOPの変更

ピンが決まったのでverilogのtopを変えてLEDをそのままGPIOから出します。
Quartusでde0_nano_soc_baseline.vを開いて、
GPIO0がコメントアウトで無効になっているのでコメントアウトを削除。
assign GPIO_0[9] = LED[0];
を追加します。

全部書くとこうなります。
長いけど変えたのは2行だけです。

de0_nano_soc_baseline.v
//--------------------------------------------------------------------------//
// Title:        de0_nano_soc_baseline.v                                       //
// Rev:          Rev 0.1                                                    //
// Last Revised: 09/14/2015                                                 //
//--------------------------------------------------------------------------//
// Description: Baseline design file contains DE0 Nano SoC                   //
//              Board pins and I/O Standards.                               //
//--------------------------------------------------------------------------//
//Copyright 2015 Altera Corporation. All rights reserved.  Altera products
//are protected under numerous U.S. and foreign patents, maskwork rights,
//copyrights and other intellectual property laws.
//                 
//This reference design file, and your use thereof, is subject to and
//governed by the terms and conditions of the applicable Altera Reference
//Design License Agreement.  By using this reference design file, you
//indicate your acceptance of such terms and conditions between you and
//Altera Corporation.  In the event that you do not agree with such terms and
//conditions, you may not use the reference design file. Please promptly                         
//destroy any copies you have made.
//
//This reference design file being provided on an "as-is" basis and as an
//accommodation and therefore all warranties, representations or guarantees
//of any kind (whether express, implied or statutory) including, without
//limitation, warranties of merchantability, non-infringement, or fitness for
//a particular purpose, are specifically disclaimed.  By making this
//reference design file available, Altera expressly does not recommend,
//suggest or require that this reference design file be used in combination 
//with any other product not provided by Altera
//----------------------------------------------------------------------------

//Group Enable Definitions
//This lists every pinout group
//Users can enable any group by uncommenting the corresponding line below:
//`define enable_ADC
//`define enable_ARDUINO
`define enable_GPIO0
//`define enable_GPIO1
`define enable_HPS

module de0_nano_soc_baseline(


    //////////// CLOCK //////////
    input                       FPGA_CLK_50,
    input                       FPGA_CLK2_50,
    input                       FPGA_CLK3_50,

`ifdef enable_ADC
    //////////// ADC //////////
    /* 3.3-V LVTTL */
    output                      ADC_CONVST,
    output                      ADC_SCLK,
    output                      ADC_SDI,
    input                       ADC_SDO,
`endif

`ifdef enable_ARDUINO
    //////////// ARDUINO ////////////
    /* 3.3-V LVTTL */
    inout                   [15:0]  ARDUINO_IO,
    inout                               ARDUINO_RESET_N,
`endif

`ifdef enable_GPIO0
    //////////// GPIO 0 ////////////
    /* 3.3-V LVTTL */
    inout               [35:0]      GPIO_0,
`endif

`ifdef enable_GPIO1 
    //////////// GPIO 1 ////////////
    /* 3.3-V LVTTL */
    inout               [35:0]      GPIO_1,
`endif

`ifdef enable_HPS
    //////////// HPS //////////
    /* 3.3-V LVTTL */
    inout                       HPS_CONV_USB_N,

    /* SSTL-15 Class I */
    output          [14:0]      HPS_DDR3_ADDR,
    output           [2:0]      HPS_DDR3_BA,
    output                      HPS_DDR3_CAS_N,
    output                      HPS_DDR3_CKE,
    output                      HPS_DDR3_CS_N,
    output           [3:0]      HPS_DDR3_DM,
    inout           [31:0]      HPS_DDR3_DQ,
    output                      HPS_DDR3_ODT,
    output                      HPS_DDR3_RAS_N,
    output                      HPS_DDR3_RESET_N,
    input                       HPS_DDR3_RZQ,
    output                      HPS_DDR3_WE_N,
    /* DIFFERENTIAL 1.5-V SSTL CLASS I */
    output                      HPS_DDR3_CK_N,
    output                      HPS_DDR3_CK_P,
    inout            [3:0]      HPS_DDR3_DQS_N,
    inout            [3:0]      HPS_DDR3_DQS_P,

    /* 3.3-V LVTTL */
    output                      HPS_ENET_GTX_CLK,
    inout                       HPS_ENET_INT_N,
    output                      HPS_ENET_MDC,
    inout                       HPS_ENET_MDIO,
    input                       HPS_ENET_RX_CLK,
    input            [3:0]      HPS_ENET_RX_DATA,
    input                       HPS_ENET_RX_DV,
    output           [3:0]      HPS_ENET_TX_DATA,
    output                      HPS_ENET_TX_EN,
    inout                       HPS_GSENSOR_INT,
    inout                       HPS_I2C0_SCLK,
    inout                       HPS_I2C0_SDAT,
    inout                       HPS_I2C1_SCLK,
    inout                       HPS_I2C1_SDAT,
    inout                       HPS_KEY,
    inout                       HPS_LED,
    inout                       HPS_LTC_GPIO,
    output                      HPS_SD_CLK,
    inout                       HPS_SD_CMD,
    inout            [3:0]      HPS_SD_DATA,
    output                      HPS_SPIM_CLK,
    input                       HPS_SPIM_MISO,
    output                      HPS_SPIM_MOSI,
    inout                       HPS_SPIM_SS,
    input                       HPS_UART_RX,
    output                      HPS_UART_TX,
    input                       HPS_USB_CLKOUT,
    inout            [7:0]      HPS_USB_DATA,
    input                       HPS_USB_DIR,
    input                       HPS_USB_NXT,
    output                      HPS_USB_STP,
`endif

    //////////// KEY ////////////
    /* 3.3-V LVTTL */
    input               [1:0]           KEY,

    //////////// LED ////////////
    /* 3.3-V LVTTL */
    output          [7:0]           LED,

    //////////// SW ////////////
    /* 3.3-V LVTTL */
    input               [3:0]           SW

);

    hps u0 (
          .led_0_led_output                (LED),                // led_0_led.output
        .hps_io_hps_io_emac1_inst_TX_CLK (HPS_ENET_GTX_CLK), //            hps_io.hps_io_emac1_inst_TX_CLK
        .hps_io_hps_io_emac1_inst_TXD0   (HPS_ENET_TX_DATA[0]),   //                  .hps_io_emac1_inst_TXD0
        .hps_io_hps_io_emac1_inst_TXD1   (HPS_ENET_TX_DATA[1]),   //                  .hps_io_emac1_inst_TXD1
        .hps_io_hps_io_emac1_inst_TXD2   (HPS_ENET_TX_DATA[2]),   //                  .hps_io_emac1_inst_TXD2
        .hps_io_hps_io_emac1_inst_TXD3   (HPS_ENET_TX_DATA[3]),   //                  .hps_io_emac1_inst_TXD3
        .hps_io_hps_io_emac1_inst_RXD0   (HPS_ENET_RX_DATA[0]),   //                  .hps_io_emac1_inst_RXD0
        .hps_io_hps_io_emac1_inst_MDIO   (HPS_ENET_MDIO),   //                  .hps_io_emac1_inst_MDIO
        .hps_io_hps_io_emac1_inst_MDC    (HPS_ENET_MDC),    //                  .hps_io_emac1_inst_MDC
        .hps_io_hps_io_emac1_inst_RX_CTL (HPS_ENET_RX_DV), //                  .hps_io_emac1_inst_RX_CTL
        .hps_io_hps_io_emac1_inst_TX_CTL (HPS_ENET_TX_EN), //                  .hps_io_emac1_inst_TX_CTL
        .hps_io_hps_io_emac1_inst_RX_CLK (HPS_ENET_RX_CLK), //                  .hps_io_emac1_inst_RX_CLK
        .hps_io_hps_io_emac1_inst_RXD1   (HPS_ENET_RX_DATA[1]),   //                  .hps_io_emac1_inst_RXD1
        .hps_io_hps_io_emac1_inst_RXD2   (HPS_ENET_RX_DATA[2]),   //                  .hps_io_emac1_inst_RXD2
        .hps_io_hps_io_emac1_inst_RXD3   (HPS_ENET_RX_DATA[3]),   //                  .hps_io_emac1_inst_RXD3

        .hps_io_hps_io_sdio_inst_CMD     (HPS_SD_CMD),     //                  .hps_io_sdio_inst_CMD
        .hps_io_hps_io_sdio_inst_D0      (HPS_SD_DATA[0]),      //                  .hps_io_sdio_inst_D0
        .hps_io_hps_io_sdio_inst_D1      (HPS_SD_DATA[1]),      //                  .hps_io_sdio_inst_D1
        .hps_io_hps_io_sdio_inst_CLK     (HPS_SD_CLK),     //                  .hps_io_sdio_inst_CLK
        .hps_io_hps_io_sdio_inst_D2      (HPS_SD_DATA[2]),      //                  .hps_io_sdio_inst_D2
        .hps_io_hps_io_sdio_inst_D3      (HPS_SD_DATA[3]),      //                  .hps_io_sdio_inst_D3

        .hps_io_hps_io_usb1_inst_D0      (HPS_USB_DATA[0]),      //                  .hps_io_usb1_inst_D0
        .hps_io_hps_io_usb1_inst_D1      (HPS_USB_DATA[1]),      //                  .hps_io_usb1_inst_D1
        .hps_io_hps_io_usb1_inst_D2      (HPS_USB_DATA[2]),      //                  .hps_io_usb1_inst_D2
        .hps_io_hps_io_usb1_inst_D3      (HPS_USB_DATA[3]),      //                  .hps_io_usb1_inst_D3
        .hps_io_hps_io_usb1_inst_D4      (HPS_USB_DATA[4]),      //                  .hps_io_usb1_inst_D4
        .hps_io_hps_io_usb1_inst_D5      (HPS_USB_DATA[5]),      //                  .hps_io_usb1_inst_D5
        .hps_io_hps_io_usb1_inst_D6      (HPS_USB_DATA[6]),      //                  .hps_io_usb1_inst_D6
        .hps_io_hps_io_usb1_inst_D7      (HPS_USB_DATA[7]),      //                  .hps_io_usb1_inst_D7
        .hps_io_hps_io_usb1_inst_CLK     (HPS_USB_CLKOUT),     //                  .hps_io_usb1_inst_CLK
        .hps_io_hps_io_usb1_inst_STP     (HPS_USB_STP),     //                  .hps_io_usb1_inst_STP
        .hps_io_hps_io_usb1_inst_DIR     (HPS_USB_DIR),     //                  .hps_io_usb1_inst_DIR
        .hps_io_hps_io_usb1_inst_NXT     (HPS_USB_NXT),     //                  .hps_io_usb1_inst_NXT

        .hps_io_hps_io_spim1_inst_CLK    (HPS_SPIM_CLK),    //                  .hps_io_spim1_inst_CLK
        .hps_io_hps_io_spim1_inst_MOSI   (HPS_SPIM_MOSI),   //                  .hps_io_spim1_inst_MOSI
        .hps_io_hps_io_spim1_inst_MISO   (HPS_SPIM_MISO),   //                  .hps_io_spim1_inst_MISO
        .hps_io_hps_io_spim1_inst_SS0    (HPS_SPIM_SS),    //                  .hps_io_spim1_inst_SS0

        .hps_io_hps_io_uart0_inst_RX     (HPS_UART_RX),     //                  .hps_io_uart0_inst_RX
        .hps_io_hps_io_uart0_inst_TX     (HPS_UART_TX),     //                  .hps_io_uart0_inst_TX

        .hps_io_hps_io_i2c0_inst_SDA     (HPS_I2C0_SDAT),     //                  .hps_io_i2c0_inst_SDA
        .hps_io_hps_io_i2c0_inst_SCL     (HPS_I2C0_SCLK),     //                  .hps_io_i2c0_inst_SCL
        .hps_io_hps_io_i2c1_inst_SDA     (HPS_I2C1_SDAT),     //                  .hps_io_i2c1_inst_SDA
        .hps_io_hps_io_i2c1_inst_SCL     (HPS_I2C1_SCLK),     //                  .hps_io_i2c1_inst_SCL

        .memory_mem_a                    (HPS_DDR3_ADDR),                    //            memory.mem_a
        .memory_mem_ba                   (HPS_DDR3_BA),                   //                  .mem_ba
        .memory_mem_ck                   (HPS_DDR3_CK_P),                   //                  .mem_ck
        .memory_mem_ck_n                 (HPS_DDR3_CK_N),                 //                  .mem_ck_n
        .memory_mem_cke                  (HPS_DDR3_CKE),                  //                  .mem_cke
        .memory_mem_cs_n                 (HPS_DDR3_CS_N),                 //                  .mem_cs_n
        .memory_mem_ras_n                (HPS_DDR3_RAS_N),                //                  .mem_ras_n
        .memory_mem_cas_n                (HPS_DDR3_CAS_N),                //                  .mem_cas_n
        .memory_mem_we_n                 (HPS_DDR3_WE_N),                 //                  .mem_we_n
        .memory_mem_reset_n              (HPS_DDR3_RESET_N),              //                  .mem_reset_n
        .memory_mem_dq                   (HPS_DDR3_DQ),                   //                  .mem_dq
        .memory_mem_dqs                  (HPS_DDR3_DQS_P),                  //                  .mem_dqs
        .memory_mem_dqs_n                (HPS_DDR3_DQS_N),                //                  .mem_dqs_n
        .memory_mem_odt                  (HPS_DDR3_ODT),                  //                  .mem_odt
        .memory_mem_dm                   (HPS_DDR3_DM),                   //                  .mem_dm
        .memory_oct_rzqin                (HPS_DDR3_RZQ),                //                  .oct_rzqin
    );

    assign GPIO_0[9] = LED[0];

endmodule

Quartusでコンパイルし直します。
sambaでfatにコピーして、rebootしておきます。

servoアプリを作る

毎回lwアプリで16進数で入力するのが面倒くさいのでアプリを作ります。
periodは常に20msなのでdutyだけms単位で指定するアプリにしましょう。

servo.c
#include <stdio.h>
#include <stdlib.h>
#include <error.h>

#include "lw_driver.h"

int main(int argc, char ** argv)
{
    double duty, clk_1sec;
    uint32_t data;

    // 引数チェック
    if(argc!=2 )
    {
        printf("servo duty(msec)\n");
        exit(EXIT_FAILURE);
    }

    if(open_lw())
        exit(EXIT_FAILURE);

    clk_1sec = 100e6;   // 100MHz
    sscanf(argv[1], "%lf", &duty);

    // period
    data = (uint32_t)(20e-3*clk_1sec);  // 20ms
    write_lw(0, data);  

    //duty
    data = (uint32_t)(duty*1e-3*clk_1sec);  // 引数ms
    write_lw(1, data);  

    close_lw();
    exit(EXIT_SUCCESS);
}

浮動小数点が使えるので計算も楽ですね。アプリの引数も浮動小数点にしています。
sambaのappにこのファイルを置いて、
Atlas-SoCのLinux上から
gcc servo.c lw_driver.c -o servo
と入力するだけでコンパイルができます。
一瞬です。もちろん再起動も不要。ソフトの開発は楽でいいよね。
FPGAのコンパイルは時間がかかって。。。。

動作確認

./servo duty[ms]
で実行できます。0.5~2.4を入れるとサーボがキュッキュと動きます。
ピタッと止まらないことがあるけど、400円にしては上出来ですかね。

./servo 0.5
05.JPG

./servo 2.4
24.JPG

./servo 1.4
14.JPG

終わりに

これで自作FPGAモジュールをCから簡単に制御できることが確認できました。
32bitのレジスタは広くていいですね。100,000,000なんてでかい数値も何も考えずに渡せます。
計算はC言語が簡単でいいし、FPGAの処理は100MHzで動きます。最強じゃないですか。
開発のしやすさはC言語の方が楽ですね。Linuxなのでセルフコンパイルも問題なくできます。
最後の最後でFPGA処理にしたいところです。

3
6
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
6