TNCTのえむけー(@mktakuya)に誘われて年越しハッカソンしてました。先週秋葉原に行った時にようやく自分用のマイコンを買ったので、とりあえずそれで遊んでみようということで、LチカからのPWM全ピン出力を試してみました。

参加表明してから思い出したのですが、そういえば10年位前の年末も「日本語プログラミング言語ひまわり(今のなでしこ)」に手を出していました。もちろん夜はまたいでないし、ましてプログラムの書き方すら分かっていなかったので、全然動かなかったのですが。笑

事の発端

30日。Twitter4Cをx64とかでも使えるように作業していた時に、ふとTLを見るとえむけーが年越しハッカソンの勧誘をやっていたので

ちょうど作業も終わったので登録してみた、というのが始まり。

 ソースコード

今回買ったマイコンはNXPのLPCXpresso LPC1769で、mbedからブートローダとかUSBフラッシュを取っ払った素のマイコン、といえば分かる人がいるかもしれません。なんでこのマイコンかというと、私が昨年度卒研でいじっていたマイコンっていうのと、USBもEthernetもなんでも使えて2.5kでとってもお買い得だから。みんなも買うといいよ!(ステマ)

で、PWMも卒研の時にいじってはみたんだけどどーもうまく出力できなくて、今回はそのリベンジといった感じでやってました。調べてるとあんまり詳しくソースとか載せてる人があんまりいないので、誰かが書いた英語のPDFとNXP公式の日本語リファレンスをとっかえひっかえしながらコードを書いてました。

やぎにいちゃんはGithubにリポジトリ立てて頑張ってましたが、私はまだGitに慣れてないのと(ぉぃ)そこまでするほどのコードでもないのでここに載せちゃいます。

    #ifdef __USE_CMSIS
    #include "LPC17xx.h"
    #endif

    #include <cr_section_macros.h>

    // TODO: insert other include files here

    // TODO: insert other definitions and declarations here

    void PWM_Reset(void) {
      LPC_PWM1 -> TCR = 0x02;
    }

    void PWM_Start(void) {
      LPC_PWM1 -> TCR = 0x09;
    }

    void PWM_init(unsigned int MR0_value, unsigned int UsePWMResister) {
      int i = 0;
      LPC_PINCON -> PINSEL4 &= ~(0x00001FFF);

      for(i = 0; i < 6; i++) {
        if((UsePWMResister & (1U << i))) LPC_PINCON -> PINSEL4 |= (0x1 << ((i * 2)));
      }

      //Counter Reset
      LPC_PWM1 -> TCR = 0x02;

      //Pre-scaler
      //Cleared
      LPC_PWM1 -> PR = 0x00;

      //Match Control Register
      //Match PWMMR0R, PWMMR0I, PWMMR0R
      LPC_PWM1 -> MCR = 0x03;

      //Counter Control Register
      //Use CounterMode
      LPC_PWM1 -> CTCR = 0x00;

      //Enable single mode ALL PWM
      LPC_PWM1 -> PCR = 0x7E00;

      //Match Register 0
      LPC_PWM1 -> MR0 = MR0_value;

      //Latch Enable
      LPC_PWM1 -> LER = 0x01;
    }

    void PWM_SetDuty(unsigned int PinNumber, unsigned int Duty) {
      switch(PinNumber) {
      case 1:
        LPC_PWM1 -> MR1 = (Duty * LPC_PWM1 -> MR0)  / 100;
        LPC_PWM1 -> LER |= 0x02;
        break;
      case 2:
        LPC_PWM1 -> MR2 = (Duty * LPC_PWM1 -> MR0)  / 100;
        LPC_PWM1 -> LER |= 0x04;
        break;
      case 3:
        LPC_PWM1 -> MR3 = (Duty * LPC_PWM1 -> MR0)  / 100;
        LPC_PWM1 -> LER |= 0x08;
        break;
      case 4:
        LPC_PWM1 -> MR4 = (Duty * LPC_PWM1 -> MR0)  / 100;
        LPC_PWM1 -> LER |= 0x10;
        break;
      case 5:
        LPC_PWM1 -> MR5 = (Duty * LPC_PWM1 -> MR0)  / 100;
        LPC_PWM1 -> LER |= 0x20;
        break;
      case 6:
        LPC_PWM1 -> MR6 = (Duty * LPC_PWM1 -> MR0)  / 100;
        LPC_PWM1 -> LER |= 0x40;
        break;
      default:
        break;
      }
    }

    int main(void) {
      int p1, p2, p3, p4, p5, p6;
      p1 = p2 = p3 = p4 = p5 = p6 = 0;
      int i, j;

      PWM_init(50000, 0x3F);

      while(1) {
        PWM_SetDuty(1, p1);
        PWM_SetDuty(2, p2);
        PWM_SetDuty(3, p3);
        PWM_SetDuty(4, p4);
        PWM_SetDuty(5, p5);
        PWM_SetDuty(6, p6);

        PWM_Start();
        p1 += 3;
        p2 += 4;
        p3 += 5;
        p4 += 6;
        p5 += 7;
        p6 += 8;

        if(p1 > 100) p1 = 0;
        if(p2 > 100) p2 = 0;
        if(p3 > 100) p3 = 0;
        if(p4 > 100) p4 = 0;
        if(p5 > 100) p5 = 0;
        if(p6 > 100) p6 = 0;

        for(i=0; i<1000; i++) {
          for(j=0; j<1000; j++);
        }
      }

      return 0;
    }

まずPWM_initでMR0の値(1MHzを分周した時のカウント値、40kHzなら1000000 / 40000 = 250)と使用するPWMピン番号(使用したいところに1を立てる)を指定して、あとはそれぞれのPWMピンに対してデューティを設定してあげればOKのようです(少なくともLEDの輝度を調整する程度には)。

よく見ると高専時代に参考にしていたサンプルコードとあまり違いがない気がしますが、あの時動かなかったのは動かなかったのではなく単に周期が細かすぎてLEDに出て来にくかっただけなのかも…とか思ったりしましたが、とりあえず手元の青色LEDはこの周期設定とプログラムで輝度が調整出来ました。

あと、Ethernet接続キットも買ったので試しにTwitter投稿を試みたのですが、uIPのドライバがmbedのLANチップ用(DP83848C)になっていてLPCXpressoにはそのままでは移植できなかったので、今回は断念しました。

さいごに&まとめ