LPC1343 I2C EEPROM通信 read

出来た事

1.I2Cを使ったEEPROMのRead(1byte Read)

(エラー処理なしの単純処理)
開発Listへ戻る

さて1byte Readの場合です。(前回1 byte write)
任意のアドレスの1 byteのデータを読み出します。
まず動作としてWriteとReadで何が違うのかを確認します。

Writeの場合(Microchip : DS21930A_JP - Page 16引用)

Readの場合(Microchip : DS21930A_JP - Page 19引用)

最初のほうはReadもWriteも同じです。
任意のアドレスを指定するところまでは同じなんですね。

その次にまたStartを入れてデバイスの指定と今度はReadを送信します。
ここのACKは(Readなので"H"と記載した矢印の右横のACK)
Readしますよ良いですか?というマスタから訊かれているのに対して
EEPROMがOK!と返事をするという意図なのでEEPROMからACKが出ます。

次はいよいよEEPROMからデータを受けることになります。
EEPROMがデータを出していてマスタ(LPC1343)がデータを受けるので
ここのACKの管理(図中のマスタ管理 No ACK部分)は、マスタ側にあります。
1.マスタがACKを返す: もっとデータ頂戴!という意味
EEPROMは次のアドレスのデータを出力する。
2.マスタがACKを返さない: もうデータは要らないよという意味。
EEPROMはもうデータを送信しない。
今回は1byte Readなので2番の動作を選択します。



code (PJTはSemihostingで作る

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

#include
#include

// Variable to store CRP value in. Will be placed automatically
// by the linker when "Enable Code Read Protect" selected.
// See crp.h header for more information
__CRP const unsigned int CRP_WORD = CRP_NO_CRP ;

#include

int main(void) {

LPC_IOCON->PIO0_4 = 0x01; // PIO0_4 as SCL
LPC_IOCON->PIO0_5 = 0x01; // PIO0_5 as SDA
LPC_SYSCON->SYSAHBCLKCTRL |= (1<<5);// Enable for I2C clock
LPC_SYSCON->PRESETCTRL |= 0x02; // De-asset I2C reset

LPC_I2C->SCLL = 350; // SCL Low
LPC_I2C->SCLH = 350; // SCL High
// Duty=50% about 100kHz@70MHz clock

NVIC_EnableIRQ(I2C_IRQn); // Enable I2C interrupt

LPC_I2C->CONCLR = 0x6c; // I2C flagClear
LPC_I2C->CONSET = 0x40; // I2C enable

LPC_I2C->CONSET = 0x20; // assert Start

// Enter an infinite loop, just incrementing a counter
volatile static int i = 0 ;
while(1) {
i++ ;
}
return 0 ;
}

void I2C_IRQHandler(void)
{
uint8_t StatValue;
int adrs=0x13;

StatValue= LPC_I2C->STAT;
switch ( StatValue )
{
case 0x08: //Now Status is Start
LPC_I2C->DAT =0xa0; //Device address 1010 000 + Write
LPC_I2C->CONCLR = 0x28; //clear interrupt flag + set next action
break;
case 0x18: //Now Status is SLA+W and Receive ACK
LPC_I2C->DAT =adrs; //Address
LPC_I2C->CONCLR = 0x08; //clear interrupt flag
break;

// ここまではWriteと同じなので説明割愛

case 0x28: //Now Status Data byte and Receive ACK
LPC_I2C->CONSET = 0x20; //Set Start again
LPC_I2C->CONCLR = 0x08;
break;
case 0x10: //Receive Repeated Start
LPC_I2C->DAT = 0xa1; //Set Device address & Read
LPC_I2C->CONCLR = 0x28;
break;
case 0x40: //Now Status is SLA+R and Receive ACK
// LPC_I2C->CONSET = 0x04; //send ACK (LPC1343 output ACK)
LPC_I2C->CONCLR = 0x08;
break;
case 0x58:
LPC_I2C->CONSET = 0x10; //send stop command
LPC_I2C->CONCLR = 0x08; //Clear interrupt & Disable I2c
while(LPC_I2C->CONSET & 0x00000010); //waiting STOP flag
LPC_I2C->CONCLR = 0x48; //Clear interrupt & Disable I2c

default:
break;
}
}

解説です。

Writeのときと変わる部分から説明します。(codeの青文字以下の説明です)

データのアドレスを指定した後
ステータスが0x28になり
割り込みが発生します。
次は再度スタートコマンドを入れなければならないのでコードは以下です。

case 0x28: //Now Status Data byte and Receive ACK
LPC_I2C->CONSET = 0x20; //Set Start again
LPC_I2C->CONCLR = 0x08;
break;

再度入力したスタートコマンドのステータスはRepeated Statとして0x10になります。
ステータス9x10で割り込みが入ると

バイスコードを指定してReadコマンドを入れます。
EEPROMのデバイスコード=1010, アドレス=000(回路でA0-A2をGNDに接続) Read=1ですから
0xa1です。


case 0x10: //Receive Repeated Start
LPC_I2C->DAT = 0xa1; //Set Device address & Read
LPC_I2C->CONCLR = 0x28;
break;

バイスアドレス+Readを送信してEEPROMからACKを受けるとステータスが0x40になって
割り込みが入ります。
次はデータを受ける番です。
連続してデータを受ける場合 (+1 アドレスに格納されたデータを受ける場合)
LPC1343からEEPROMにACKを返せばいいのです。その場合は
LPC_I2C->CONSET = 0x04; でAckを返す設定をすればよいのですが
今回は1byte Readなので不要です(だからコメントアウトしてます)
割り込みクリアをすれば、No-Ackでデータを受けてくれます。

case 0x40: //Now Status is SLA+R and Receive ACK
// LPC_I2C->CONSET = 0x04; //send ACK (LPC1343 output ACK)
LPC_I2C->CONCLR = 0x08;
break;

データを受けてACKを返さなかった場合のステータスは0x58です。
割り込みが入ると
すでにDATAレジスタにはEEPROMから受けたデータが入っていますので
Printfで表示します。
そして、Stop commandを入れて、割り込みクリア です。
Stopコマンドが入るのをステータスレジスタで確認できるまでまって
I2C オフです。

LPC_I2C->CONSET = 0x10; //send stop command
LPC_I2C->CONCLR = 0x08; //Clear interrupt & Disable I2c
while(LPC_I2C->CONSET & 0x00000010); //waiting STOP flag
LPC_I2C->CONCLR = 0x48; //Clear interrupt & Disable I2c


結果としてコンソールにWriteのときに書いた2cが表示されています。

LPC1343 I2C EEPROM通信 write

出来た事

1.I2Cを使ったEEPROMのWrite

(LPC1343がマスタ : エラー処理なしの単純処理)
開発Listへ戻る

I2Cにチャレンジです!

Philipsが開発した、世の中で長年使われている通信仕様 I2C
代表的な使用例はEEPROMですね。

それ以外でいうと、温度センサーやADコンバータなどの
インターフェースでも使われています。


手始めにEEPROMの 1byte write/readをやろうと思います。今回はWriteです。

EEPROMは容量によってコントロール方法が少し違います。
具体的に言うと 256byte-16k byteと32k-512k byteに分かれます。

アドレスを指定して書き込み・読み出しを行う際に
16kまでは8bitでアドレス指定が出来るのに対し、32k-512kのアドレス指定はそれ以上必要になります。
I2Cは8bit単位でデータを通信するのでちょっと変わってきます。

以下はMicrochipの仕様書から(DS21930A_JP - Page 16引用)
16kまではAddress byteが1ですが、32-512kはLow/High 2つのAddress byteが必要です。


このエントリでは4k byteのEEPROMを使います。(上の図の6.1です)

(秋月で買うEEPROMは60円とかですが64k byte以上のようです。(図6.2)

接続図!
プルアップ抵抗を忘れずに!

I2C通信ではICがHigh,Lowを出力すのではなく、
ICはLowを出力 or High-Z状態を作ります。
全てのICがHigh-Z状態であればプルアップ抵抗があるためにHighになります。
このためIC同士のコンフリクトがありません。賢いですね。
(あるICがHighを出力し、あるICがLowを出力するとショートする
この状態をコンフリクトといいってます)



I2C write動作解説

先ほどの図6.1まさにこいつをそのまま行います。
(DS21930A_JP - Page 16引用)

I2CはSDA,SCLの2本の線で複数のICをコントロールすることが出来ます。
したがって、複数のICに対した想定が必要です。
ちなみにコントロールする側(今回の場合のLPC1343)をマスタ
コントロールされる側をスレーブといいます。

Start:
マスタからの信号です。
今からスタートするぞー準備しろという指令がスタート信号です。

Control byte :
これはマスタが点呼を取っているようなイメージです。
今から○○ICをコントロールするけど居ます??みたいな感じで訊いています。

上の図の場合は1010というデバイス=EEPROMで***というアドレスのICに
Writeしたいんだけど居る??と訊いているのです。

Control byteの7:4bitはデバイスを示していてEEPROMの場合は1010です。
バイスごとに違うので他のデバイスを扱う場合はDatasheetで確認する必要があります。

3:1bitはたとえばEEPROMを2つ以上コントロールする場合にどのICか指定する場合に使います。
つまり、ICのユーザ側の設定です。
今回は回路図の接続を見てもわかるようにA2-A0をGNDしていますので "000"です。

0 bitはWrite or Readです 0がWrite 1がReadです。

ACK:
ICのお返事です。アクノリッジ、アックといいます。

1010というデバイス=EEPROMで***というアドレスのICに
Writeしたいんだけど居る?? と訊かれて

ハイハイ私です!と返事をするという事です。したがって、この場合はEEPROMからの信号です。
(ReadのときなどマスタからACKを返さなければならないこともあります)
ACKは0です。

Address byte:
マスタ側からの信号で、
じゃあ△△アドレスに書き込みたいんだけど良い?と訊いています。

その次のACKはEEPROMから返事の信号で
ACKがある=0ということは良いよ!! といっています。

Data byte:      
マスタからの信号です。
このデータ書き込んで!という指令です。

その次のACKはEEPROMから返事の信号で
ACKがある=0ということは書き込んだよ!!ということです。

Stop:
終了!!

です。

LPC1343でのI2C通信(Write)
ユーザマニュアルUM10375 Rev. 3 ― 14 June 2011 
ページ 228 of 368が動作の該当ページです。

赤の部分だけやります。

青矢印の部分はSLA(スレーブアドレス)= 上の図でいうControl byteを送信したのに
ACKが返ってこなかった場合です。

製品設計などになると、そのような意図しないところにも対応しなければならないでしょうが
今回は面倒なので割愛。アクシデントは考えません。

赤枠部分の説明

Sおよび下の丸の中の08H:
Sがスタート信号送信で○はステータスレジスタの値です。
ステータスが08Hになります。

08を確認した後でSLA+Write = 1010 000 0
(1010=EEPROM 000=回路上のA0-A2の設定(今回接続は3つともGNDなので000) 0=Write)
を送信します。

ACKを確認するとステータスレジスタは18Hになります。

18Hを確認すると今度は図6-1でいうAddress byteを送信です。
0Hだとちゃんと制御できているかあいまいなので今回は12Hにしてみましょう。

ACKを確認するとステータスレジスタは28Hになります。

28Hを確認すると今度は書き込むデータを送信します。
今回は適当に19Hにします。

ACKを確認するとまたステータスレジスタは28Hになります。

2回目の28Hを確認したら終了のSTOP信号を入力します。

code

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

#include
#include

__CRP const unsigned int CRP_WORD = CRP_NO_CRP ;

int main(void) {
LPC_IOCON->PIO0_4 = 0x01; // PIO0_4 as SCL
LPC_IOCON->PIO0_5 = 0x01; // PIO0_5 as SDA
LPC_SYSCON->SYSAHBCLKCTRL |= (1<<5);// Enable for I2C clock
LPC_SYSCON->PRESETCTRL |= 0x02; // De-asset I2C reset

LPC_I2C->SCLL = 350; // SCL Low
LPC_I2C->SCLH = 350; // SCL High
// Duty=50% about 100kHz@70MHz clock

NVIC_EnableIRQ(I2C_IRQn); // Enable I2C interrupt

LPC_I2C->CONCLR = 0x6c; // I2C flagClear
LPC_I2C->CONSET = 0x40; // I2C enable

LPC_I2C->CONSET = 0x20; // assert Start
// Enter an infinite loop, just incrementing a counter
volatile static int i = 0 ;
while(1) {i++ ;}
return 0 ;
}

void I2C_IRQHandler(void)
{
uint8_t StatValue;
int adrs=0x13;
int dat =0x2C;
static int t=1;

StatValue= LPC_I2C->STAT;
switch ( StatValue )
{
case 0x08: //Now Status is Start
LPC_I2C->DAT =0xa0; //Device address 1010 000 + Write
LPC_I2C->CONCLR = 0x28; //clear interrupt flag + set next action
break;
case 0x18: //Now Status is SLA+W and Receive ACK
LPC_I2C->DAT =adrs; //Address
LPC_I2C->CONCLR = 0x08; //clear interrupt flag
break;
case 0x28: //Now Status is sending Data byte and Receive ACK
if(t==1){ // 1st : Data byte is address
LPC_I2C->DAT = dat; // write data
LPC_I2C->CONCLR = 0x08;
}else{ // 2nd : Data byte is write data
LPC_I2C->CONSET = 0x10; //send stop command
LPC_I2C->CONCLR = 0x08; //Clear interrupt
while(LPC_I2C->CONSET & 0x00000010); //waiting STOP flag
LPC_I2C->CONCLR = 0x48; //Clear interrupt & Disable I2c }
++t;
break;
default:
break;
}
}

解説です。

まずはIOPINの設定とI2Cへのクロック供給、リセット解除です。
ユーザマニュアルUM10375 Rev. 3 ― 14 June 2011 
ページ 207 of 368 の13.2 Basic configurationをやっているだけです。

LPC_IOCON->PIO0_4 = 0x01; // PIO0_4 as SCL
LPC_IOCON->PIO0_5 = 0x01; // PIO0_5 as SDA
LPC_SYSCON->SYSAHBCLKCTRL |= (1<<5);// Enable for I2C clock
LPC_SYSCON->PRESETCTRL |= 0x02; // De-asset I2C reset

次はクロック周波数,DUTYの設定です。
ユーザマニュアル ページ 213 of 368
13.8.5.1 Selecting the appropriate I2C data rate and duty cycleのTable223をみて
100kHz程度になるようにしています。
(LCP1343のSYSTEM clockは72MHzです)

LPC_I2C->SCLL = 350; // SCL Low
LPC_I2C->SCLH = 350; // SCL High
// Duty=50% about 100kHz@70MHz clock

そしてI2Cの割り込みを有効にします。
ステータスが変わると割り込みが入ります。

NVIC_EnableIRQ(I2C_IRQn); // Enable I2C interrupt


一応フラッグを全てクリアーにしてからI2Cを有効にします。

LPC_I2C->CONCLR = 0x6c; // I2C flagClear
LPC_I2C->CONSET = 0x40; // I2C enable


そしてI2C動作スタート!
スタートコマンド送信!

LPC_I2C->CONSET = 0x20; // assert Start

スタートコマンドを送信するとステータスが0x08=スタートコマンドを送信したよ!
になります。そして割り込みが入るわけです。

switch文でStatus毎に動作を割り振っていますが、上から順に実行されるようにしてます。
スタートコマンドを送信した=0x08割り込みが入った後は
バイスアドレスとWriteコマンドを送信しなければなりませんので
LPC_I2C->Datに 0xa0を入れておきます。
その後でスタートフラグと割り込みフラグを解除します。
割り込みを解除すると次の動作に行きます。
バイスアドレス+write=SLA+Writeを送信します。

StatValue= LPC_I2C->STAT;
switch ( StatValue )
{
case 0x08: //Now Status is Start
LPC_I2C->DAT =0xa0; //Device address 1010 000 + Write
LPC_I2C->CONCLR = 0x28; //clear interrupt flag + set next action
break;

SLA+WriteをEEPROMが受けてACKを出したことをLPC1343が検出するとステータスが0x18になります。
そこで割り込みがまた発生します。
次は書き込むアドレスを送信します。
アドレスは int adrs=0x13; で指定しました。(意味なし気分で決めた値)
DATレジスタにアドレスを入れて、割り込みフラグを解除でアドレスデータを送信します。

case 0x18: //Now Status is SLA+W and Receive ACK
LPC_I2C->DAT =adrs; //Address
LPC_I2C->CONCLR = 0x08; //clear interrupt flag
break;

アドレスデータをEEPROMが受けてACKを出したことをLPC1343が検出するとステータスが0x28になります。
(Data byte in I2DAT has been transmitted ACK has been received.)
次の書き込みデータを送信する場合もステータスは0x28なので
カウンタで最初の0x28と次の0x28を区別できるようにしました。
最初の0x28で発生した割り込みでは
次にwriteデータを送信します。
writeデータは int dat =0x2C; で指定しました。(意味なし気分で決めた値)
DATレジスタにデータを入れて、割り込みフラグを解除でwriteデータを送信です。

case 0x28: //Now Status is sending Data byte and Receive ACK
if(t==1){ // 1st : Data byte is address
LPC_I2C->DAT = dat; // write data
LPC_I2C->CONCLR = 0x08;

最後。
writeデータをEEPROMが受けてACKを出したことをLPC1343が検出するとステータスがまた0x28になります。
2回目の0x28です。
これで終わりなのでStopコマンドを送信して
ストップフラグが立つのをまって
I2Cをdisableします。この部分はNXPのExample codeからもってきました。
(2012.3.4修正)


}else{ // 2nd : Data byte is write data
LPC_I2C->CONSET = 0x10; //send stop command
LPC_I2C->CONCLR = 0x08; //Clear interrupt
while( LPC_I2C->CONSET & 0x00000010); //waiting STOP flag
LPC_I2C->CONCLR = 0x48; //Clear interrupt & Disable I2c }


ちなみに次の送信データセットと割り込みフラグ解除の書く順番ですが
データセット→フラグ解除にすべきです。
逆にしても通常で流すと結果は同じになりました。
しかし、デバッグモードで1Step毎に進むと、逆に書いた場合は結果が期待値と異なります。

期待値が出る書き順
LPC_I2C->DAT =0xa0; //Device address 1010 000 + Write
LPC_I2C->CONCLR = 0x28; //clear interrupt flag + set next action

デバッグモードでは期待値が出ない書き順
LPC_I2C->CONCLR = 0x28; //clear interrupt flag + set next action
LPC_I2C->DAT =0xa0; //Device address 1010 000 + Write

LPC1343 32bit Timerの割り込み処理 

出来た事

1.32bit Timerの割り込み処理

  • NVIC_EnableIRQ(TIMER_32_1_IRQn)で割り込み有効化
  • cr_startup_lpc13.cの中で該当の関数を探す
  • Example codeでリセットなどを確かめる


開発Listへ戻る

今日は割り込みにチャレンジ!!

書き方なんぞ良くわかってなかったですがイロイロ調べて何とかできました。


NVIC_EnableIRQ(TIMER_32_1_IRQn); 

この記載で割り込みを有効にする。(32bitタイマーの割り込み有効)

そして、割り込みの関数の記載の仕方は
Projectのscrの中にある
cr_startup_lpc13.c に入ってます。

それらしき名前のものできっと正しいです。



ちなみに32bit タイマーの割り込み関数は

void TIMER32_0_IRQHandler(void)
void TIMER32_1_IRQHandler(void)

の2つで、今回はCT32B1を使うのでTIMER32_1_IRQHandlerの方を使います。


今回はPWMのDutyを変えてLEDの調光を行いました。

回路は前回と同じです。AD2(P1_1)を使います(=CT32BMAT0)


動作は以下の図のようにPWMのdutyを変えて明るさを変化させます。

PWMの1周期は100Hzです。
0.1秒毎に5%ずつDutyを減らして5%になったら95%にして繰り返しです。

95%→90%→・・・・5%→95%

この0.1秒毎にDutyを変化させることをTimerの割り込みで実行します。

コードです。

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

#include
#include


__CRP const unsigned int CRP_WORD = CRP_NO_CRP ;

int main(void) {
volatile static int i = 0 ; // dummy counter

NVIC_EnableIRQ(TIMER_32_1_IRQn); // enable Timer32_1 interrupt handler

// 32bit counter
LPC_SYSCON->SYSAHBCLKCTRL |=0x400; // Timer32B1 Turn ON
LPC_IOCON->JTAG_TDO_PIO1_1 =0x83; // PIO1_1 as 32B1_MAT0
LPC_TMR32B1->PR =7200-1; // 10kHz(Max 32bit dec:4294967295)
LPC_TMR32B1->PWMC |=0x01; // MAT0 as PWM
LPC_TMR32B1->MCR =0x600; // MR3 as Period of 32B1 and interrupt
LPC_TMR32B1->MR0 =50; // PWM High
LPC_TMR32B1->MR3 =100; // PWM Period 10kx100=100Hz
LPC_TMR32B1->TCR =2; // TCR Reset
LPC_TMR32B1->TCR =1; // TCR start

// Enter an infinite loop
while(1) { // Infinite loop
i++; // dummy count.
}
return 0 ;
}

void TIMER32_1_IRQHandler(void) {
static int t = 0 ; // interrupt counter
static int duty=50; // duty counter

if(t>=9){ //100 Hz x10=0.1s
t=0;
LPC_TMR32B1->MR0 =duty;
if(duty>=95){
duty=5;
}else{
duty=duty+5;
}
}else{
++t;
}
LPC_TMR32B1->IR=0x08; //clear interrupt flag
}

最初に以下でタイマーの割り込みを有効にしておく必要があります。
NVIC_EnableIRQ(TIMER_32_1_IRQn); // enable Timer32_1 interrupt handler

MR3でタイマーのリセットと割り込みを発生させる設定です。
LPC_TMR32B1->MCR =0x600; // MR3 as Period of 32B1 and interrupt

PWMの周期は10kHz x100カウントで100Hzになるようにしました。
LPC_TMR32B1->PR =7200-1; // 10kHz(Max 32bit dec:4294967295)
LPC_TMR32B1->MR3 =100; // PWM Period (Max 32bit dec:4294967295)

割り込みの中はPWM 100cyc毎にDutyを5%ずつ上げて95%になったら5%に戻す記載ですが
特記すべきところは以下です。
LPC_TMR32B1->IR=0x08; //clear interrupt flag

Example codeにも合ったので追加しときました。
今回はMR3を割り込みに使っているので対応するIRレジスタに1を書いてリセットしています。

ちなみに0を書いたり、この記載がなかったりするとおかしな動作をしました。

LPC1343 32bit TimerでPWM

出来た事

1.32bit Timerを使ったPWM出力をLEDで確認
2.16bit Timerを使ったPWM出力をLEDで確認

開発Listへ戻る

今日はタイマーを使ってみたいと思います。
タイマーの結果はPWMとして出力しLEDのon-offで動作確認をします。

下の図はユーザーマニュアルからLPC1343のタイマーの構成部分を抜き出したものです。
32bitタイマーが2個,16bitタイマーが2個 計4個のタイマーがあります。

またタイマーだけではなく、
外部信号の立上がり立下りのカウンターとしても使えるようです。
タイマー or カウンターの出力は MAT[3:0]の4本
設定数に達するとHigh,Low,反転やPWM出力などが選べるようです。
外部信号をカウントする場合はCAP0から入力するようです。



今回はCT32B1とCT16B1のタイマーを使い
それぞれのMAT[0]をPWM出力として
LEDをon-offさせます。

構成図からもわかるようにタイマーおよびMAT,CAPは
メインの演算部とは独立した機能ですから設定してしまえば
自動的ににPWMが出ます。


接続図は以下のようになります。

LEDの点灯コントロールは以下のようにします。



タイマーの構成のイメージは以下のようです。

PCLKがタイマーユニットに供給されるクロックです。

LPC_SYSCON->SYSAHBCLKDIV で設定されるクロック周波数です。
通常はこのレジスタは1で、
その場合はLPC1343のSystem clock周波数=72MHzです。

 PCLK= 72MHz / SYSAHBCLKDIV [MHz]です。

これを変更すると他にも影響するので私は72MHzで使います。

PCがプリスケールカウンタです。
PCがPCLKによって設定値までカウントアップしたときにTCが+1され,PCがリセットされます。
(PRレジスタで設定)

たとえばPCLK=72MHzのときにPR=71とすると1MHz周期でTCがカウントアップすることになります。
(PCは0からスタートなので-1する必要がある)

TCがタイマーカウンターです。
TCに関してはリセットするカウント数,割り込みを発生させるカウント数
あわせて4つ設定できます。

MR0〜MR3 がTCのタイミングを設定するレジスタです。

MCRレジスタでMR0-3のどれをリセットにするか、割り込みにするかを設定できます。

今回はPWM出力にします。
MAT0端子を出力にする場合以下のようになります。

MAT0を使う場合はHighに変化するポイントをMR0で指定することが決まっているので
必然的にTCをリセットするカウント数はMR0以外を指定する必要があります。

コードです。

ifdef __USE_CMSIS
#include "LPC13xx.h"
#endif

#include
#include


__CRP const unsigned int CRP_WORD = CRP_NO_CRP ;


int main(void) {
volatile static int i = 0 ; // dummy counter


// 32bit Timer
LPC_SYSCON->SYSAHBCLKCTRL |=0x400; // Timer32B1 Turn ON
LPC_IOCON->JTAG_TDO_PIO1_1 =0x83; // PIO1_1 as 32B1_MAT0
LPC_TMR32B1->PR =72000-1; // 1kHz(Max 32bit dec:4294967295)
LPC_TMR32B1->PWMC |=0x01; // MAT0 as PWM
LPC_TMR32B1->MCR =0x400; // MR3 as Period of 32B1
LPC_TMR32B1->MR0 =1000; // PWM High duration=MR3-MR0
LPC_TMR32B1->MR3 =3000; // PWM Period (Max 32bit dec:4294967295)
LPC_TMR32B1->TCR =2; // TCR Reset
LPC_TMR32B1->TCR =1; // TCR start

// 16bit Timer
LPC_SYSCON->SYSAHBCLKCTRL |=0x100; // Timer16B1 Turn ON
LPC_IOCON->PIO1_9 = 0x001; // PIO1_9 as CT16B1_MAT0
LPC_TMR16B1->PR = 7200-1; // 10kHz (Max 16bit dec:65535)
LPC_TMR16B1->PWMC = 1; // MAT0 as PWM
LPC_TMR16B1->MCR = 0x80; // MR2 as Period of 16B1
LPC_TMR16B1->MR0 = 10000; // PWM High duration=MR2-MR0
LPC_TMR16B1->MR2 = 15000; // PWM Period (Max 16bit dec:65535)
LPC_TMR16B1->TCR = 2; // TCR Reset
LPC_TMR16B1->TCR = 1; // TCR Start


// Enter an infinite loop
while(1) { // Infinite loop
i++; // dummy count.
}
return 0 ;
}

コード中のコメントでほぼ問題ないと思います。

32bit Timerの場合ですが
MAT0を使うためにTCのリセットはMR0意外にしなければならないので

LPC_TMR32B1->MCR =0x400; MR3をリセットにしました。

LPC_TMR32B1->PWMC |=0x01; はMAT0をPWMにするという事です。


ちなみに、16bit timerの場合はTCだけではなくPC(プリスケールカウンタ)まで16bitです
したがって、72MHzのPCLKの場合はPCで1kHzを作ることは出来ません。
(1kHzを作るには 72000-1にする必要がありますが16bitだと65535がMaxのため)

こいつに気づかずに少々ハマったので念のため・・・・

LPC1343 SWDIOをGPIOに設定したときのリカバリ法

本日、何気なくLPC1343のPIO1_3をGPIOに変更してしまいました。


プログラムを実行させると・・・・

あれ?デバッグが出来ない・・・

おかしい、もう一度プログラムをダウンロードしよう!!

LPCLinkが反応しない・・・


ダウンロードも出来なくなりました。。。


なんで??

仕様書を見てみると、プログラムのダウンロードやデバッグ
SWDという方法をとっているらしく
下の図のSWDIO,SWCLK,SWO,RESETの信号を使っているらしいということがわかりました。


そしてGPIOに変更したPIO1_3のオリジナル機能がSWDIOでした。。

デバッグやダウンロードに使うピン機能を変更したので
出来なくなったということか・・・

この記載がいけなかったわけです。
LPC_IOCON->ARM_SWDIO_PIO1_3 = 0x81; NG記載


しかしですね、
SWDIOの機能をGPIOに変更したのでデバッグが効かなくなるのはわかります。
デバッグ用の機能をOFFにしたわけですからね。。

でも、一度機能を変更したらダウンロードできなくなるのはおかしい!!
絶対クレームになるし、リカバリできないわけがない。


そう思って仕様書を調べてみると、

リカバリー出来ました!

LPCexpresso.getting.startedにRecovery の項目があり
ISPピンをGNDにしろとあります。

そしてユーザーマニュアルにはISPピンはPIO0_1だとありました。

LPC1343のPIO1_3(SWDIO)をGPIOに設定してしまった場合のリカバリ法!!

PIO0_1をGNDに接続してPIO1_3をSWDIO設定にしたプログラムをダウンロード(Debug)
(PIO0_1=FT/GPIOのシルク部)

これでリカバリーが出来ました。

たぶんPIO0_10(SWCLK)をGPIOに変更しても同様なことが発生するでしょうが
チキン野郎なので実験はしません。。。



でも、よくよく見てみるとPIO1_3って
AD4として使いたかったり
Dataバスとして使いたかったりもします。

その場合はたぶん、別のピンを設定ピンのようにすることで対応可能だと思います。

たとえばPIO2_10とかをInputに設定しておいて
PIO2_10がLOWのときはPIO1_3をAD4に設定し
PIO2_10がHighのときはPIO1_3をSWDIOに設定するという記載にすればよさそうです。

またの機会に試してみます。

LPC1343 7seg LEDを制御する on-off駆動をして電力セーブする

7segのLEDをon-offさせて電力をセーブします。

コンセプトは下の図のようなon-off駆動です。
前回記事では
8の場合1秒間ずっと8を点灯させています。(左側)
1秒間に何回もon-offさせる(右側)事で電力が減ります(明るさも当然減ります)


さらに今回は電流変化も平均化させます。
単純に8を点灯⇔消灯の繰り返しでは
電流0→70mAの変化の繰り返しになります。
大きな電流変化は電源の不安定化につながります。

これを対策するために下図の右側のように8の部分部分を点灯させていきます。
平均電流は右も左も同じですが、電流変化が少ない分、回路にやさしい駆動方法です。



コード

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

#include
#include

__CRP const unsigned int CRP_WORD = CRP_NO_CRP ;

int main(void) {
volatile static uint32_t period; // Assigned to a variable for debug
volatile static int i = 0 ; // dummy counter

// Configure PORT PIO2_0 - 2_6
LPC_IOCON->PIO2_0 &= 0x00; // Configure PIO2-0 Pullup off
LPC_IOCON->PIO2_1 &= 0x00; // Configure PIO2-1 Pullup off
LPC_IOCON->PIO2_2 &= 0x00; // Configure PIO2-2 Pullup off
LPC_IOCON->PIO2_3 &= 0x00; // Configure PIO2-3 Pullup off
LPC_IOCON->PIO2_4 &= 0x00; // Configure PIO2-4 Pullup off
LPC_IOCON->PIO2_5 &= 0x00; // Configure PIO2-5 Pullup off
LPC_IOCON->PIO2_6 &= 0x00; // Configure PIO2-6 Pullup off
LPC_GPIO2->DIR |= 0x7F; // PIO2-0-6 as output

// Configure System tick timer in 1msec period
period = SystemCoreClock / 1000; // Period for 1msec SYSTICK
SysTick_Config(period); // Configuration


// Enter an infinite loop
while(1) { // Infinite loop
i++; // dummy count.
}
return 0 ;
}

void SysTick_Handler(void) {
static int count = 0; // Software counter
static int sec=0; // second counter
static int cnt2 = 0; // 7ms counter
int data,mask;

mask=0;
if (++count >= 1000) { // wait for 1000 system ticks =1s
if(sec>=10){
sec = 0;
}
count = 0; // Reload counter
++sec;
}


switch (sec) {
case 0:

data = 0x02; break;
case 1:
data = 0x2F; break;
case 2:
data = 0x41; break;
case 3:
data = 0x05; break;
case 4:
data = 0x2C; break;
case 5:
data = 0x14; break;
case 6:
data = 0x10; break;
case 7:
data = 0x27; break;
case 8:
data = 0x00; break;
case 9:
data = 0x04; break;
default:
data = 0x02; break;
}

if(++cnt2>=7){
cnt2=0;
}

mask |= (1<DATA = data | ~mask;
}

前回記事からの比較で説明します。

period = SystemCoreClock / 1000; // Period for 1msec SYSTICK
10ms毎に割り込み→1ms毎に割り込みに変更しました。
LEDの点滅が見える事を避けるためです。


static int count = 0; // Software counter
count変数をintにしました。 1ms割り込みに変更したために、1000カウントしなければなりません。
unit8では255でカウントオーバーするためです。


static int cnt2 = 0; // 7ms counter
cnt2を追加しました。 0-6をカウントするものです。7segの各LEDのマスクに使います。


int data,mask;
data=表示する7seg デコード, mask=マスク信号です。


mask |= (1<マスク信号作成です。1になっているビットだけ点灯させるイメージです。
cnt2=0 : mask=000 0001;
cnt2=1 : mask=000 0010;
   ・
   ・
cnt2=6 : mask=100 0000;


LPC_GPIO2->DATA = data | ~mask;
実際はGNDで点灯ですのでmask信号は反転させてorをとっています。


これで動作します。
見た目は点滅していることはわかりません。

しかし、以下2行の値を変更すると、点滅速度が遅くなり
人間の目でもどのように動いているか確認できるようになります。

period = SystemCoreClock / 10; //(1000→10 : 1ms→100ms)
if (++count >= 100) { // wait for 1000→100 system ticks

LPC1343 7seg LEDを制御する count up動作&code

開発Listへ戻る

7Seg LED制御の動作とコード解説です。


動作
単純なカウントアップを行います。
1秒毎に1→2・・とカウントアップし9までいくと次は0に戻ります。

コード
今回もベースはエレキジャックさんの記事です。
SysTick timerの使い方と
GPIOの使い方が掲載されていました。
下のコードは7seg用にモディファイしたものです。

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

#include
#include

__CRP const unsigned int CRP_WORD = CRP_NO_CRP ;

int main(void) {
volatile static uint32_t period; // Assigned to a variable for debug
volatile static int i = 0 ; // dummy counter

// Configure PORT PIO2_0 - PIO2_6 as output
LPC_IOCON->PIO2_0 &= 0x00; // Configure PIO2-0 Pullup off
LPC_IOCON->PIO2_1 &= 0x00; // Configure PIO2-1 Pullup off
LPC_IOCON->PIO2_2 &= 0x00; // Configure PIO2-2 Pullup off
LPC_IOCON->PIO2_3 &= 0x00; // Configure PIO2-3 Pullup off
LPC_IOCON->PIO2_4 &= 0x00; // Configure PIO2-4 Pullup off
LPC_IOCON->PIO2_5 &= 0x00; // Configure PIO2-5 Pullup off
LPC_IOCON->PIO2_6 &= 0x00; // Configure PIO2-6 Pullup off
LPC_GPIO2->DIR |= 0x7F; // PIO2_0 - PIO2_6 as output

// Configure System tick timer in 10msec period
period = SystemCoreClock / 100; // Period for 10msec SYSTICK
SysTick_Config(period); // Configuration

// Enter an infinite loop
while(1) { // Infinite loop
i++; // dummy count.
}
return 0 ;
}

void SysTick_Handler(void) {
static uint8_t count = 0; // Software counter
static int sec=0; // second counter
if (++count >= 100) { // wait for 100 system ticks =1s
if(sec>=10){
sec = 0;
}
count = 0; // Reload counter
switch (sec) {
case 0:
LPC_GPIO2->DATA = 0x02;
break;
case 1:
LPC_GPIO2->DATA = 0x2F;
break;
case 2:
LPC_GPIO2->DATA = 0x41;
break;
case 3:
LPC_GPIO2->DATA = 0x05;
break;
case 4:
LPC_GPIO2->DATA = 0x2C;
break;
case 5:
LPC_GPIO2->DATA = 0x14;
break;
case 6:
LPC_GPIO2->DATA = 0x10;
break;
case 7:
LPC_GPIO2->DATA = 0x27;
break;
case 8:
LPC_GPIO2->DATA = 0x00;
break;
case 9:
LPC_GPIO2->DATA = 0x04;
break;
default:
LPC_GPIO2->DATA = 0x02;
break;
}
++sec;

}
}

最初はGPIOの設定です。PIO2_0 - PIO2_6までをOUTPUTとして使用します。
まずは以下のコードでPull-upをはずします。
LPC_IOCON->PIO2_0 &= 0x00; // Configure PIO2-0 Pullup off

データシートを見るとリセットバリューでPull up設定になっていることがわかります。
放置しても良いといえばよいですが今回は外しました。


次にoutputの設定を行います。
PIO2_0-PIO2_6まで7本 outputにしますので 111 1111=7Fです。
LPC_GPIO2->DIR |= 0x7F; // PIO2_0 - PIO2_6 as output

SysTick timerは10ms毎に割り込みを入れるようにしているので
100カウント=1秒で secをカウントup sec=10でリセット(=0)するようにしました。

そしてsecの値を7seg LED表示するようにデコード値をcase switch文で割り振りました。


ここまでで、7seg LEDはカウントアップ動作をします。
しかし、今回 LEDの電流を10mA程度にしたにもかかわらず明るすぎでした。
また、1LEDで10mAですから7segの場合8を表示すると70mAも流れることになります。
USB powerは500mAですからその14%です。これではたくさんの7segLEDを使用することが出来ません。

そこで次は高速でLEDをオン-オフさせ電流をセーブしてみようと思います。