3
6

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

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

3
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

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?