いかにして問題を解くか

このサイトはGoogle Analyticsを利用しています。

Spacemacs に Specman mode を導入する

概要

有名なパッケージではないため自動でインストールできない major-modeである specman-mode を手動で使えるように設定する。

手順

以下の通りに進みます。

  1. specman-mode.el のダウンロード
  2. load-path とモードの追加

specman-mode.el のダウンロード

24 以降に対応した specman-mode を Cadence forum から入手する。

Specman Mode for Emacs - Functional Verification Shared Code - Cadence Technology Forums - Cadence Community

適当な場所(.spacemacs.d/lisps など)に置く。

load-path とモードの追加

emacs を立ち上げて

space f e d

で .spacemacs または .spacemacs.d/init.el を開く。 そして dotspacemacs/user-config に以下を追加する

  (add-to-list 'load-path "~/.spacemacs.d/lisps")
  ;;specman-mode
  (autoload 'specman-mode "specman-mode" "Specman code editing mode" t)
  (setq auto-mode-alist
       (remove-duplicates
        (append (list
                 (cons "\\.e\\'" 'specman-mode)
                 (cons "\\.ecom\\'" 'specman-mode)
                 (cons "\\.erld\\'" 'specman-mode))
                auto-mode-alist)))

これで使えるようになります。 もろもろ不慣れで幼稚なミスで autoload できずにはまった。 ※ load-path に設定するパスをミスしていた。

Spacemacs

目的

emacs + vim という最新の環境に開発環境をアップデートする。 ちなみに私はそこまでカスタマイズしていないemacsを15年くらいは使っている。 vi はほぼ使ったことが無い。

きっかけ

最新のemacs環境を調べていたら、世の中は vim が主流になっていることが判明した。 確かに周りでも vim を使う人が多いとは感じていた。 そろそろ vim に切り替えるかと思っていたら

qiita.com

こんな記事に出会って、今の私に一番違和感なく環境をアップデートできそうだと感じた。 これをきっかけに vim でも編集ができるようになれればと思う。

概要

emacs にもろもろの拡張をセットにしてインストールして、 カスタマイズしまくったものを spacemacs と呼んでいる。 そのインストールは spacemacs という拡張lisp で一括インストールできる。

spacemacs は Githubにあり、

github.com

からダウンロードできる。 基本的には素の emacs に対して ~/emacs.d を上記のもので置き換えるだけ。 15年前には考えられないような状態なので、特にカスタマイズしていないで使ってきた私にはちょっと衝撃だった。

インストールの詳細はREADME.mdを参照してもらえれば問題ない。

機能のこと

素のemacsを拡張しているが、それ自体は spacemacs の外で開発されている。 主な拡張は

日本語入力

Windows から Linux に入って Xwindowを飛ばしているので、mozcをインライン表示したい。 また、fcitx + mozc と emacs25 を使うように Linux 環境は構築してある。

インストール

emacs-mozc-bin パッケージを linux に導入する。emacs-mozcは導入しない。 また、.spacemacs または spacemacs.d/init.el の user-config 内に

(defun dotspacemacs/user-config ()
  ;; 省略
  ;;mozc
    (when (require 'mozc nil t)
    (setq default-input-method "japanese-mozc")
    (global-set-key [zenkaku-hankaku] 'toggle-input-method) ;; 全角半角で切り替えられる
    (defvar mozc-candidate-style) ;; avoid compile error
    (if (require 'mozc-popup nil t)
        (setq mozc-candidate-style 'popup)
      (setq mozc-candidate-style 'echo-area)))
  ;;省略
)

のように記述する

おまけ

入力モードの切り替えが暴走する事がある。それを防ぐため .bashrc に

xset -r 49

を追加した。

Spacemacs 操作方法備忘録

個人的な備忘録

neotree の表示

Space f t

Raspberry pi で AP のレジスタをC言語で直接変更してモーターを PWM で操作する

目的

PWM でDCモータを操作します。 また、フルブリッジのモータードライバICを使います。

注意点

Linuxシステムコールを含む(mmap関数)ので、root 権限が必要なため sudo コマンドを利用する必要があります。 プログラム名を sample.c とするならば、

 $ gcc sample.c
 $ sudo ./a.out

のように実行してください。

モータドライバIC

TA7291P を使いました。

モータードライバー TA7291P (2個入): 半導体 秋月電子通商 電子部品 ネット通販

端子記号 端子番号 端子説明
GND 1 GND
OUT1 2 出力端子
NC 3
Vref 4 制御電源端子
IN1 5 入力端子
IN2 6 入力端子
VCC 7 ロジック側電源端子
VS 8 出力側電源端子
NC 9
OUT2 10 出力端子

回路の概要

本当は RaspberryPi 3 を使っています。

f:id:katogiso-tech:20171112200721p:plain

main() プログラム

正転と逆転をGPIO20/21 を使って切り替えています。

void main()
{
  volatile unsigned *addr;
  int count;

  //--------------------------
  // GPIO
  gpio = io_mapping(GPIO_BASE);

  // set GPIO18 mode to ALT5(PWM0)
  *(gpio + 0x4/4) = (*(gpio + 0x4/4) & ~(0x7 << 24 )) | (0b010 << 24);

  // set GPIO20/21 mode to Output
  *(gpio + 0x8/4) = (*(gpio + 0x8/4) & ~(0x7 << 0 )) | (0b001 << 0);
  *(gpio + 0x8/4) = (*(gpio + 0x8/4) & ~(0x7 << 3 )) | (0b001 << 3);

  //--------------------------
  // Clock
  clk  = io_mapping(CLK_BASE);
  // add the offset address of pwm clk manager
  clk += 0xA0/4;
  // disable
  *clk = CLK_PASSWD + (0x1<<5);
  // divided by 192
  *(clk+0x4/4) = CLK_PASSWD + (192 << 12);
  // choose oscillator(19.2MHz) as source
  *clk = CLK_PASSWD + 0x1 + (0x1 << 4);

  //--------------------------
  // PWM
  pwm  = io_mapping(PWM_BASE);
  *pwm = 0; // Disable
  usleep(10);

  *(pwm + 0x10/4) = 8; // set RNG
  *(pwm + 0x14/4) = 6; // set DAT
  *pwm = 0x1;  // Enable

  //--------------------------
  // drive motor
  for( count = 0; count < 2; count++ ){
    // set
    if( count % 2 ){
      *(gpio + 0x1C/4) = 0x1 << 20;
    } else {
      *(gpio + 0x1C/4) = 0x1 << 21;
    }
    sleep(3); 

    // clear
    *(gpio + 0x28/4) = 0x1 << 21;
    *(gpio + 0x28/4) = 0x1 << 20;
    sleep(3); 
  }

 *pwm = 0; // Disable
  usleep(10);
}

電圧、電流波形

ChannelA が電圧(PWM)で、ChannelB は電流波形です。 また、電流波形は1Ωの抵抗両端で測定しました。

f:id:katogiso-tech:20171112201431p:plain

Raspberry pi で AP のレジスタをC言語で直接変更して PWM を操作する

やること

Raspberry Pi 用の特別なライブラリとドライバを利用せずにPWMをC言語で操作する。

注意点

Linuxシステムコールを含む(mmap関数)ので、root 権限が必要なため sudo コマンドを利用する必要があります。 プログラム名を sample.c とするならば、

 $ gcc sample.c
 $ sudo ./a.out

のように実行してください。

main()

IC 18pin (RaspberryPi の端子だと 12pin) をALT5モードにして、PWMを発生させる。 PWM のサブモードは

  1. ある期間(M, RNG)内で、バランスよく N(N, DAT) 回 Highとなる
    • サイトによっては balanced mode と記載あり
    • 仕様書にはPWM algorithm
  2. ある期間(M, RNG)内で、最初に N(N, DAT) 回 Highとなる
    • サイトによっては legacy mode と記載あり
    • 仕様書には M/S transmission

の2つあります。 正式な名称は不明で、完全な仕様書を読むと記載があるのかもしれません。

実際の波形

上記のモードだけを切り替えた波形を参考に添付します。 他の設定値は同じです。

balanced mode

f:id:katogiso-tech:20171112170346p:plain

legacy mode

f:id:katogiso-tech:20171112170350p:plain

サンプルコードではクロックのソースを Oscillator(19.2MHz)にして これをもとに 192 分周して100kHzにしています。 また、PWM algorithm( balanced mode ) です。

void main()
{

  //------------------------------------
  // set GPIO18 mode to ALT5(PWM0)
  //------------------------------------
  gpio = io_mapping(GPIO_BASE);
  *(gpio + 0x4/4) = (*(gpio + 0x4/4) & ~(0x7 << 24 )) | (0b010 << 24);

  //------------------------------------
  // set source clock 
  //------------------------------------
  clk  = io_mapping(CLK_BASE);
  clk += 0xA0/4;
  // Oscillator 19.2MHz
  *clk         = CLK_PASSWD + (0x1<<5);
  *(clk+0x4/4) = CLK_PASSWD + (192 << 12);
  *clk         = CLK_PASSWD + 0x1 + (0x1 << 4);

  //------------------------------------
  // set pwm
  //------------------------------------
  pwm = io_mapping(PWM_BASE);
  *pwm = 0;   // Disable
  usleep(10); // wait for stable 

  *(pwm + 0x10/4) = 4; // set RNG
  *(pwm + 0x14/4) = 2; // set DAT

  *pwm = 0x1; // Enable 
}

物理アドレスのアクセス用マッピング

過去記事を参照してください。

katogiso-tech.hatenablog.com

使っているレジスタ

Clock 関連

ADDRESS Name Description Size Read/Write
0x3F10_1000 CM_PWMCTL PWM Clock Control 32 R/W
0x3F10_1004 CM_PWMDIV PWM Clock Divisors 32 R/W
CM_PWMCTL

名前は類推で、記載を見つけられず WiringPi ソースコードから該当アドレスを確認しました。 ただし、CM_GPnCTL レジスタの記述があり、レジスタの操作は仕様書が参考になります。

Clock 系のレジスタへの書き込みはbit[31:24]に0x5a を同時に書き込む必要があります。

使っている bit field は

  • SRC bit[3:0]

    • 0 : GND
    • 1 : oscillator
    • 4 : PLL
    • 他にも色々ある
  • ENAB bit[4]

    • 1 : Start
    • 0 : Stop
  • KILL bit[5]

    • 1 : stop and reset
    • 0 : no action

です。

CM_PWMDIV

名前は類推で、記載を見つけられず WiringPi ソースコードから該当アドレスを確認しました。 ただし、CM_GPnDIV レジスタの記述があり、レジスタの操作は仕様書が参考になります。

クロックの分周を決めるレジスタです。

使っている bit field は

  • DIVI bit[23:12]
    • Integer part of divisor

PWM 設定関連

ADDRESS Name Description Size Read/Write
0x3F20_C000 CTL PWM Channel1 Range 32 R/W
0x3F20_C010 RNG1 PWM Channel1 Range 32 R/W
0x3F20_C014 DAT1 PWM Channel1 Data 32 R/W

CTL

仕様書からの抜粋です。 初期値はどれも 0x0 で、 bit0(Enable) と bit7(PWMのモード切り替え) を使えばほぼ大丈夫なはず。

bit# Name Description Read/Write
7 MSEN1 Channel 1 M/S Enable
0: PWM algorithm is used
1: M/S transmission is used.
RW
4 POLA1 Channel 1 Polarity
0 : 0=low 1=high
1: 1=low 0=high
RW
1 MODE1 Channel 1 Mode
0: PWM mode
1: Serialiser mode
RW
0 PWEN1 Channel 1 Enable
0: Channel is disabled
1: Channel is enabled RW 0x0
RW

RNGn

PWM の期間をこのレジスタ値で分割します。 32 未満の値で設定してください。

DATn

RNGn で設定した期間のうちHiとする回数を設定できます。

全容

RaspberryPi pwm sample

Raspberry pi で AP のレジスタをC言語で直接変更して GPIO を操作する

やること

Raspberry Pi 用の特別なライブラリとドライバを利用せずにGPIOをC言語で操作する。

その他の普通の手段

注意点

Linuxシステムコールを含む(mmap関数)ので、root 権限が必要なため sudo コマンドを利用する必要があります。 プログラム名を sample.c とするならば、

 $ gcc sample.c
 $ sudo ./a.out

のように実行してください。

main()

IC 20pin (RaspberryPi の端子だと 38pin) を操作して、High/Low をずっと繰り返す。

アドレスをいちいち addr/4 していますが、仕様書に記載されている byte アドレスを 32bit(4byte)アドレスに変換しているためです。 C言語の int は今回 32bitなので、アドレスを保持しているポインタに 1 を加算すると 32bit 分ポインタがずれます。

void main()
{
  int count;
  volatile unsigned *addr;

  gpio = io_mapping(GPIO_BASE);

  // set GPIO20 to output mode ( bit[2:0] ). offset is 0x8( byte address ) .
  *( gpio + 0x8/4 ) = (*( gpio + 0x8/4 ) & ~(0x7)) | (0x1 << 0);

  for( count = 0 ; ; count++){
    usleep(1000); 
    addr = ( count % 2 ) ? gpio + 0x1C/4 :  gpio + 0x28/4 ;
    *addr = 0x1 << 20;
  }
}

物理アドレスのアクセス用マッピング

過去記事を参照してください。

katogiso-tech.hatenablog.com

使っているレジスタ

ADDRESS Name Description Size Read/Write
0x3F20_0008 GPFSEL2 GPIO Function Select 2 32 R/W
0x3F20_001C GPSET0 GPIO Pin Output Set 0 32 W
0x3F20_0028 GPCLR0 GPIO Pin Output Clear 0 32 W

GPFSELn

SoC の端子は色々な機能を切り替えられるようになっていて、 RaspberryPi も同じようになっている。 今回は output モードに切り替えていて、GPFSEL2 の bit[2:0] に 1 を書き込んでいます。

GPSETn

Write Only のレジスタで、1 の書き込みで対応するGPIOが High になる。

GPCLRn

Write Only のレジスタで、1 の書き込みで対応するGPIOが Low になる。

その他

レジスタの値をそのまま反映する GPLEVn もあるが、他のGPIOの値を含めてレジスタReadして対応するbitだけ変更する必要がり(Read modified write)、少し手数が増えて複雑となります。

全容

RaspberryPi gpio sample

Raspberry pi で AP のレジスタを操作する前に

Linux からのレジスタ操作

仮想アドレス

OS上の通常のプログラムはハードウェアのアドレス(物理アドレス)からは隔離されていて、 仮想的なアドレス空間上で動作しています。 その仮想アドレスがOSとHW(MMU)で変換されて、一部が物理アドレスです。

そのため、AP の仕様書に記載されている物理アドレスへアクセスするにはひと工夫が必要です。

mmap()

Linuxシステムコールmmap()を使って、物理アドレスへアクセスすることができます。

Man page of MMAP

今回は SRAM の開始アドレスが仕様書から 0x0000_0000 であることがわかるので、 これを基準にしてGPIOなどへアクセスします。

mmap() の概要

物理アドレスをプログラムの仮想アドレス上に割り当てることができます。 (これ以外の目的に使うこともあります。) ただし、ファイルシステムや、HW の制約により Block/Pase size に気をつける必要があります。 Block/Page size は予め決められたアドレスマップ上の区切りのことです。

サンプルコード

volatile unsigned * io_mapping(int base_addr)
{
    char *gpio_mem, *gpio_map;
    
    if (!mem_fd) {
      mem_fd = open("/dev/mem", O_RDWR|O_SYNC);
      check((mem_fd < 0), "can't open /dev/mem \n");
    }
    
    gpio_mem = malloc(BLOCK_SIZE + (PAGE_SIZE-1));
    check( ( gpio_mem == NULL), "allocation error \n");

    // Make sure pointer is on 4K boundary
    if ((unsigned long) gpio_mem % PAGE_SIZE)
      gpio_mem += PAGE_SIZE - ((unsigned long)gpio_mem % PAGE_SIZE);
    
    // Now map it
    gpio_map = (char *)mmap((caddr_t)gpio_mem, 
                            BLOCK_SIZE, 
                            PROT_READ|PROT_WRITE, 
                            MAP_SHARED|MAP_FIXED, 
                            mem_fd, 
                            base_addr );

    check( ((long)gpio_map < 0), "mmap error \n");

    return (volatile unsigned *)gpio_map;
}

io_mapping() は、 SRAM のアドレスを基準にして、アクセスしたい領域のベースアドレスをオフセットにして 物理アドレスの割当用に確保した仮想アドレス空間gpio_memを戻り値とします。

Raspberry pi の AP に関して

Broadcom 2835

Raspberry pi のAP(CPU)公式ドキュメント

場所が少しわかりにくいのでリンクを記載します。 Top ベージからは、"Help" > "Documentation" > "Hardware" > "Raspberrypi" です。

www.raspberrypi.org

ここから "BCM2835" > "Peripheral specification" にあります。

このドキュメントの課題

  1. 情報が不完全で必要としている情報も欠落している
  2. 誤記もある
  3. AVR のドキュメントと比較すると随分とわかりにくい

レジスタのアドレスなど参考になるサンプルコード

wiring Pi

wiringpi.com

このライブラリの作者はいろいろなFAQ/Forumで回答者としてアドバイスされています。 細かいことを Google 検索するとかなりの確率でお世話になる。

調べてわかったこと(備忘録)

Peripheral Base Address ( Physical )

上記公式ドキュメントと

https://www.raspberrypi.org/documentation/hardware/raspberrypi/bcm2836/QA7_rev3.4.pdf

で確認できます。

PWM pin

BCM2835 で GPIO18 を使って、モードは ALT5 にする。

PWM モード

2つのモードがある。

PWM algorithm

単位期間内のHigh/Low Cycle をバランスよく配置する。 DCモータの駆動のように平均電圧をなんとかするだけなら、こちらで使用した方が良いかもしれない。

MS algorithm

単位期間内のHigh/Low cycle が各々固まっている。 単位期間内のHigh期間が終わってから、Low 期間となる。 サーボモータの回転角を PWM 波形で制御する場合はこちらが制御しやすいだろうと思う。

Clock source

PWM のカウンタクロックは主に下記の通りとなる。 ただし、PLL の周波数はOSで設定されている通りなので、 下記周波数は調査する必要がある。

  • Oscillator = 19.2MHz
  • PLLA = 500MHz?