UMEHOSHI ITA TOP PAGE COMPUTER SHIEN LAB
[UMEHOSHI ITA]の制御で使っているIC「PIC32MX270F256B-I/SO」のフラッシュメモリには、テスト用プログラムが書き込まれいています。
以降では、このプログラムを「テスト・ウメ・フラッシュ」と呼ぶことにして解説します。
また、「テスト・ウメ・フラッシュ」を利用したユーザー用のプログラムを
「ウメ・エディットプログラム」と呼ぶことにします。
「ウメ・エディットプログラム」の開発では「umehoshiEditツール」が必要で、
その取得や最初の操作情報は、こちらを参照してください。
(「PICKit3などの書き込みツール」をお持ちの方で、「テスト・ウメ・フラッシュ」を利用しないで、
「MPLAB X IDE」の開発環境ですべてをプログラミングする場合の情報ではありません。)
「テスト・ウメ・フラッシュ」をどのように利用して、「ウメ・エディットプログラム」を
作るかの解説で、「umehoshiEditツール」の使用例を示しています。
各サンプルは、このWebページ上でドラック&コピーして、貼り付けしてご利用ください。
各種確認プログラム | 左記に必要な部品の追加例 |
---|---|
(D1のLEDを使う) | |
とくに必要ありません。 (D1のLEDは、あるとよい) | |
PWM対応の部品追加例 | |
ADC 対応の部品追加例 | |
BEEP SWITCH 対応の部品追加例 | |
Reset SW, Type-A, CN2 部品追加例 | |
U20,D4,D5,NPN, D3 部品追加例 | |
CN11,CN-12 部品追加例 | |
U19(ESP32) 部品追加例 | |
U17にRN4020の部品を追加する例 |
[UMEHOSHI ITA]基板内「テスト・ウメ・フラッシュ」には、接続しているUSBのホストに対いて、文字列を送信する命令が組み込まれており、
その使い方です。割り込みの基本も解説しています。
まず、「umehoshiEditツール」を起動して、USBを接続して
対応するCOMポートへの接続を行います。
このプロジェクトフォルダは、[samples/2_msg1]にあります。
これをプロジェクト用フォルダに設定します。
そして、次のようにmsg1.cを編集状態にします。
次にこれをビルドして、
それで生成された「msg.c.hex」を開きます。(次のイメージになります。)
次にこのUME専用HEXファイルを転送して、
実行させます。(次のイメージになります。)
実行で、 0 +1 +2 +3 +4 +5 +6 +7 +8 +9 ENDが表示されて、
nの値が繰り返しで0から順次変化して、繰り返し後のEND出力が確認できています。
R00800050000061 START:80005000そこで、再び[Excute]ボタンで実行すると次のようになります。
R00800050000061
START:80005000
0 +1 +2 +3 +4 +5 +6 +7 +8 +9R00800050000061
START:80005000
これは、2回目の実行で、前の実行で受信した結果を表示している挙動です。
「umehoshiEditツール」の[Communication]タブの表示は、ラインバッファで得た結果を表示していることに
注意してください。
つまり「umehoshiEditツール」は、
"\r\n"まで受信してから その1行の受信文字列を表示するようになっているのです。
逆に言うと"\r\n"を受信するまで、蓄えるだけで表示しません。
「umehoshiEditツール」において、この蓄えられるバッファのサイズは4096byteになっています。
よって、"\r\n"を含めないで4096byteサイズ以上になる文字列を出力するとエラーが生じます。
#include <xc.h> #include <stdlib.h> #include "common.h" __attribute__((address( 0x80005000 ))) void start (void); void start() { _RB15 = ! _RB15;// 出力を反転 ★ int n; for (n =1; n <= 500; n++){ _send_decimal( n, 4); // n の10進文字列を4文字幅で送信文字列を登録 _send_char('\r'); // '\r'の1byte文字の送信を登録 _send_char('\n'); // '\n'の1byte文字の送信を登録 } }この実行結果は、次のように正しく動くはずです。(最初の表示を省略して、最後の方だけ示しています。)
+497 +498 +499 +500
さて上記において、forの繰り返し条件を無くしてfor (n =1; ; n++){と変更すると
どうなるのでしょうか?
実行すると、応答メッセージが表示しません。固まってしまします。
結果の上記のように、まったく反応しません。USBポートも見えなくなり、暴走しているような挙動です。
このように、無限ループを作ってしまうと、[UMEHOSHI ITA]内部のUSB処理ループ処理に戻りません。
このような状態で動作する部分は、平行して動作する[UMEHOSHI ITA]内部割り込み処理しか動作できません。
(別ページで紹介するBEEPなどは、割込みで動作するので、このような状態でも動作します。)
さて、_send_string、_send_decimal、_send_charなどの関数は、呼び出し時点で送信する命令ではありません。
これらの関数は、文字列を送信用のバッファに登録する処理です。
ですからstart関数がリターンしてから、バッファに登録してある文字列が送信される仕組みになっています。
ですから、start関数内にループがあってリターンしなければ、そこで処理が進まないことになります。
また、このstart関数内が無限ループを作らない場合でも、処理が長すぎると、内部USB処理が滞り、正しく動作しなくなります。
つまり、このstart関数の処理は、可能な限り速くリターンするように作らなければなりません。
次の流れで、赤の矢印が「Execute」ボタンによる絶対アドレスの利用者プログラム呼び出しの流れです。
(上記は簡略して示していますが、USBのループ内分岐はかなり複雑です。
_send_接頭辞の関数は、USBのホストに送信する文字列をキューに、登録する処理で、
USB処理ループの中で、キューから取り出して送信しています。)
もう一つ注意しなければなりません。
上記プログラムにおいて、forの繰り返し条件をfor (n =1; n <= 600 ; n++){と変更すると
どうなるのでしょうか? 結果は次のような表示で、途中で停止していまいます。(最後の方だけ表示します。)
+503 +504 +505 +506 B ERR ([B ERR]の送信はバッファオーバーフローのエラーメッセージ)この状況は、内部のキュー構造のバッファ容量限界から生じます。
上記のような長い連続的な処理を実現するためには、[UMEHOSHI ITA]内部のUSB処理ループからの起動ではなく、
ハード的に一定間隔でスタートするようなタイマー割込み処理から呼び出します。
そしてタイマーの繰り返しを使うので、その利用者のプログラムでは可能な限り繰り返しを使わないで進むように作り方にします。
それは次のイメージです。
下記の青の流れが「Execute」ボタンによる絶対アドレスによるUSBのループからの呼び出しで、
タイマー処理の準備と、割込み許可を行います。
この許可により、赤の流れのタイマー割り込みが一定周期で実行する挙動が始まります。
上記のタイマー割込みを紹介する前に、簡単な割込み例(LED点滅)を示します。
この割り込みは、[UMEHOSHI ITA]内のTimer4の割込みを使っています。
#include <xc.h> #include "common.h" __attribute__((address( 0x80005000 ))) void start (void); void start() { T4CONbits.ON = 1;// Start timer4割込みをONにする。 }
Timer4の割込みをONにしているだけで、1秒間隔のLEDが点滅します。(割り込みの詳細はこちら)
この割込みは、_HANDLES[_IDX_TIMER_4_FUNC] に記憶される関数が、
0.05ミリ秒ごとに実行されており、内部で1000回数えたごとに
LEDのON/OFFを切り変えることで実現しています。
この_HANDLES[_IDX_TIMER_4_FUNC]の処理内容を、
次のように600まで少しずつ表示するように変更する例を以下に示します。
下記の表示が目標です。
+1 +2 ・ (表記省略 割り込みで表示 ) +598 +599 +600下記の処理は、0.05ミリ秒ごとに繰り返して呼び出されます。速すぎるので1000回に一回だけ 「1増やして表示する」処理になっています。
#include <xc.h>
#include "common.h"
void timer4()
{
static int count = 0;
if(++count % 1000 != 0) return;
// count が 1000の倍数時だけ(0.5秒ごと)に以下を実行
static int n=0;
++n;
_RB15 = ! _RB15;// 出力を反転 ★
if( _request_acc_outbuff(_ID_ACCESS_T4_TASK) == 0 ) return;
_send_decimal( n, 4); // n の10進文字列を4文字幅で送信文字列を登録
_send_char('\r'); // '\r'の1byte文字の送信を登録
_send_char('\n'); // '\n'の1byte文字の送信を登録
_set_break_buffer();// 内部出力バッファを強制出力(flashのような機能)
_release_acc_outbuff(_ID_ACCESS_T4_TASK);
if( n >= 600 ) T4CONbits.ON = 0;// timer4割込みオフ
}
__attribute__((address( 0x80005000 ))) void start (void);
void start()
{
_HANDLES[_IDX_TIMER_4_FUNC] = timer4;// デフォルトで0.00005秒ごとに呼び出される関数に、上記独自関数を登録
T4CONbits.ON = 1;// timer4割込みオン
}
幾つかの新しい関数を使っているので、それを説明します。
まず、USBへの出力を登録する命令(_send_decimal,_send_char,_set_break_bufferなど)を、割込み内で使う場合の注意です。
これらの命令は、出力バッファを管理するカウンタなどの変数を操作します。
これらを、タイマー割込みで使っている訳ですが、それと平行して出力情報がバッファに在ればUSBへ出力するための「USB制御用ループ」が動作しています。
「USB制御用ループ」がバッファ操作をしている途中で、タイマー割込みによる出力データ登録のバッファ操作が行われると、どうなるでしょうか?
バッファを管理する情報が壊れてしまうタイミングがありますね!
つまり、『「USB制御用ループ」のバッファ操作』と『タイマー割込みのバッファ操作』では排他制御が必要なのです。
この排他制御の関数が、_request_acc_outbuff(_ID_ACCESS_T4_TASK)で、これが1であればアクセス権を得たことになります。
このアクセス権は、_release_acc_outbuff(_ID_ACCESS_T4_TASK)で解放するまで、
USBの処理ループで「USBのバッファ操作」をしないように作られています。
逆に言うと、_request_acc_outbuff(_ID_ACCESS_T4_TASK)が0の時、USBへの出力を登録する命令(_send_decimal,_send_char,_set_break_bufferなど)を
実行しないようにプログラミングしなければならないということです。
そして、_request_acc_outbuff(_ID_ACCESS_T4_TASK)が1でアクセス権を得た場合は、速やかにバッファ操作をして
_release_acc_outbuff(_ID_ACCESS_T4_TASK)で解放しないと、平行して動作する他の進行が進まなくなることに注意してください。
なお、引数の_ID_ACCESS_T4_TASKは、Timer4割り込み専用で、割込みの種類ごとにいくつかはcommon.hで定義で決めています。
次の注目は、「_set_break_buffer();// 内部出力バッファを強制出力(flashのような機能)」です。
実は、_send_char命令は登録してもあるサイズまで溜まらないと、出力されない構造で作られています。
(効率的な出力制御のためにそうなっています。
複数の文字の出力を登録する他の_send_decimal命令などは、
内部の最後に_set_break_buffer()命令が使われているので、_set_break_buffer()を使う必要がありません。)
上記の「_set_break_buffer();// 内部出力バッファを強制出力(flashのような機能)」のコードをコメントにして実行すると、
最後 +600の表示をしないで終わることが確認できるでしょう。
その後再び実行すると、前に出力登録した +600がでてくる挙動になるでしょう。
さて、次に重要な注目点を示します。上記の0.5秒ほとの制御をなくして、0.05ミリごとの高速出力にするとどうなるのでしょう。
次のコードです。
#include <xc.h> #include "common.h" void timer4() { static int n=0; //if(_get_capacity() < 10) return; //残りの出力バッファブロックが10未満なら出力しない。 ++n; _RB15 = ! _RB15;// 出力を反転 ★ if( _request_acc_outbuff(_ID_ACCESS_T4_TASK) == 0 ) return; _send_decimal( n, 4); // n の10進文字列を4文字幅で送信文字列を登録 _send_char('\r'); // '\r'の1byte文字の送信を登録 _send_char('\n'); // '\n'の1byte文字の送信を登録 _set_break_buffer();// 内部出力バッファを強制出力(flashのような機能) _release_acc_outbuff(_ID_ACCESS_T4_TASK); if( n >= 600 ) T4CONbits.ON = 0;// timer4割込みオフ } __attribute__((address( 0x80005000 ))) void start (void); void start() { _HANDLES[_IDX_TIMER_4_FUNC] = timer4;// デフォルトで0.00005秒ごとに呼び出される関数に、上記独自関数を登録 T4CONbits.ON = 1;// timer4割込みオン }
この実行結果は、USB接続しているHOSTのマシン性能に依存するので、必ずしも次のようにはならないのですが、当方のPCでの最後の表示は次の結果になりました。
+300 +301 +302 +303 B ERR ([B ERR]の送信はバッファオーバーフローのエラーメッセージ)
前述よりも、速いタイミングでエラーが起きています。どうしてでしょうか?
上記のtimer4()は割込みで、0.05ミリ秒ごとに1行のUSB出力の登録を行っているのですが、登録の割込み間隔が速すぎるため
それを出力し終わる前に、次の出力登録が行われて、バッファに溜まりすぎてオーバーフローしているのです。
さて、次に//if(_get_capacity() < 10) return; //残りの出力バッファブロックが10未満なら出力しない。 のコメントを
外してコードを有効にして実験してみましょう。
この結果もHOSTのマシン性能に依存するのですが、当方のPCでの次のように最後の表示まで得られました。
_get_capacity()は出力バッファの残り容量を示すパラメタ返します。
出力バッファが足りない状態で、出力すると「B ERR」のエラーになるため、そうならないように出力を制限するifの制御です。
+596 +597 +598 +599 +600
_get_capacity()の比較判定の値はプログラムの仕方や環境によって異なるため、適正値は実験的に探ってください。
バッファが空の時が最大値で 512 の戻り値です。目安として一つの出力命令で 1 を消費します。
内部で「B ERR」の表示判定は2で行われています。この例では余裕をもって10にしました。
しかし、出力文字数が多い場合や、送出間隔が短くてHOSTの受け取りが間に合わないケースなどで不具合が起きえます。
USBのCDCプロトコルでは115200bps Parity無し 8bit 1StopBits 固定の非同期通信で、HOSTが受け取るべきタイミングで受け取ることを期待して制御しています。
しかし連続して高速にHOSTに送る処理があると、HOSTが情報を取り損なうケースが生じます。
この不具合時、送信が終わるの待つシーケンスから抜けることができなくなります。
そうなった時には、「B ERR」の表示も無しで、通信が止まる不具合が生じます。
この状態に陥った時でUSBループが動く時、LEDが0.1ミリ秒間隔程度の高速点滅で知らせる仕組みが組み込まれています。
ですが、限度を超えた連続送信登録では、この機能も働かなくなります。
割り込みの初期化もカスタマイズする例を以下に示します。
これは直接にタイマー制御レジスタを変更しています。
この設定の意味が必要な方は、別途にチップのマニュアル「60001105G_JP.pdfの制御レジスタ」を参照してください。
(これはMicrochip社の資料でインターネットで探すことができるでしょう)
#include <xc.h> #include "common.h" void timer4() //割り込み関数 { static int n=0; int size = _get_capacity(); if( size < 10) return; //残りの出力バッファブロックが10未満なら出力しない。 ++n; if( _request_acc_outbuff(_ID_ACCESS_T4_TASK) == 0 ) return; _send_hex_hi( n );//上位16bitの16進4桁表示 _send_char(':'); _send_hex_low( n );//下位16bitの16進4桁表示 _send_string(" size="); _send_decimal( size, 10); _send_string("\r\n"); _release_acc_outbuff(_ID_ACCESS_T4_TASK); if( n >= 0x020000 ) T4CONbits.ON = 0;// timer4割込みオフ nが0x020000まで繰り返す。 } void init_timer4() //割り込みの初期化もカスタマイズする関数 { T4CON =0x00000030;//typeB,16bit, [1:255]プリスケール TMR4=0x00000000; //16bitタイマの設定値(レジスタがカウントアップ) PR4=1999; //これは(1/40e6)*(1999+1)=0.05)ミリ秒 この値を減らせば、不具合を知らせるLED点滅が確認できるでしょう。 IPC4bits.T4IP = 6; // Set priority level = 6(このチップ最大優先度は7ですが、) IPC4bits.T4IS = 0;// Set sub-priority level = 3(最大値) IFS0bits.T4IF = 0;// Clear the timer interrupt status flag IEC0bits.T4IE = 1;// Timer4 Enable(割込み許可) T4CONbits.ON = 1;// Start timer4 // これ以降で、T4CONで作られる周期で、TMR4をカウントアップして、MR4がPR4と一致すると割り込みが発生します。 // なお、MR4がPR4と一致時に、TMR4は0にリセットされるため、MR4の設定周期で割込みが発生します。 } __attribute__((address( 0x80005000 ))) void start (void); void start() { _HANDLES[_IDX_INIT_TIMER_4_5] = init_timer4;// 割り込み初期化関数のデフォルトを変更 _HANDLES[_IDX_TIMER_4_FUNC] = timer4;// 割り込み関数のデフォルトを変更登録 init_timer4(); T4CONbits.ON = 1;// Start timer4 }
実行例です。カウント値を16進で表示し、10進でバッファ・ブロックの残り数を表示しています。
不具合発生に近い状態ではバッファ・ブロックの残り数が小さくなっていているのが分かります。
0000:0001 size= +512 0000:0002 size= +507 0000:0003 size= +502 0000:0004 size= +497 0000:0005 size= +492 ・・ 0001:EFCD size= +10 0001:EFCE size= +10 0001:EFCF size= +10
上記のような動作は、PC側の環境で違うでしょう。
19999の設定で割込み間隔が設定できます。
(不具合が生じる限界の設定値は、処理内容によって異なります。
余裕を持った設定を探してください。)
なお上記コードでは、16進文字列送信登録の、_send_hex_hi、_send_hex_lowもを紹介しています。
以下は、比較的難しい技術情報です。必要に応じてご覧ください。
デフォルトのTimer4,5の割り込みで、呼び出されるコード内容を示します。
// Timer4 の割り込みから呼びだされるサンプル関数--------------------------------- void timer4_sample()//デファオルと0.05ミリ秒 { static int count=0; if(++count == 10000){//0.5秒ごと _RB15 = ! _RB15;//点滅周期:1秒. count = 0; } } // Timer5 の割り込みから呼びだされるサンプル関数--------------------------------- void timer5_sample()//デファオルと0.05ミリ秒 { static int count=0; if(++count == 10000){//0.5秒ごと _RB15 = ! _RB15;//点滅周期:1秒. count = 0; } }
これらの関数は、次の割込みハンドラから呼び出しています。
handlers配列要素に上記関数へのポインタが記憶されて、それで上記を呼び出しています。
つまり、例えばhandlers[_IDX_TIMER_4_FUNC]
の内容を、希望の関数に置き換えることで、独自のタイマー割り込み処理が可能になっています。(例)
void __ISR(_TIMER_4_VECTOR, IPL1SOFT) Timer4Handler(void)
{
IFS0bits.T4IF = 0; // Clear the OC4 interrupt flag(この設定は次の割込みを可能にする)
handlers[_IDX_TIMER_4_FUNC](); //timer4_sample()//デファオルと0.05ミリ秒
}
void __ISR(_TIMER_5_VECTOR, IPL7SOFT) Timer5Handler(void)
{
IFS0bits.T5IF = 0; // Clear the OC5 interrupt flag(この設定は次の割込みを可能にする)
handlers[_IDX_TIMER_5_FUNC](); // timer5_sample()//デファオルと0.05ミリ秒
}
上記の割込みハンドラを使う割込みの初期化関数は、次のようになっています。 (Timer4は割込みレベルは番に大きな優先度で、Timer5は最下位の優先度になっています。)
void init_timer_4_5(){ // ---- Timer4を使う指定 ここから T4CON =0x00000000;//typeB,16bit, [1:1]プリスケール TMR4=0x00000000; //16bitタイマの設定値(レジスタがカウントアップ) PR4=1999; // =(1999+1)*(1/40e6) これは0.05ミリ秒 IPC4bits.T4IP = 6; // Set priority level = 6(優先度ですが変更は不可) IPC4bits.T4IS = 0;// Set sub-priority level = 3(最大値) IFS0bits.T4IF = 0;// Clear the timer interrupt status flag IEC0bits.T4IE = 1;// Timer4 Enable(割込み許可) //T4CONbits.ON = 1;// Start timer4 // ---- Timer4を使う指定 ここまで // ---- Timer5を使う指定 ここから T5CON =0x00000000;//typeB,16bit, [1:1]プリスケール TMR5=0x00000000; //16bitタイマの設定値(レジスタがカウントアップ) PR5=2000; // =0.0005/(1/40e6) これは0.05ミリ秒 IPC5bits.T5IP = 1; // Set priority level = 1(最小値の優先度ですが変更は不可) IPC5bits.T5IS = 3;// Set sub-priority level = 0(最小値) IFS0bits.T5IF = 0;// Clear the timer interrupt status flag IEC0bits.T5IE = 1;// Timer5 Enable(割込み許可) //T5CONbits.ON = 1;// Start timer5 // ---- Timer5を使う指定 ここまで }
Timer4,Timer5ともにデフォルトでは起動していません。コメント部の例えば
T4CONbits.ON = 1;のコードで割り込み処理がONします。
それを行う例は、上記のここで示しています。
なお、上記関数(init_timer_4_5())へのポインタは、初期設定で
_HANDLES[_IDX_INIT_TIMER_4_5]に記憶されて動作しています。
つまり、_HANDLES[_IDX_INIT_TIMER_4_5] = 独自の割り込み初期化関数のポインタ;のような変更も可能で、
それにより、タイマー時間を変更できます。
なお、その場合Timer4とTimer5の両方を変更することになります。
(割込みハンドラのIPL1SOFTやIPL7SOFTの書き換えができないので、優先度設定は変更できません。)
また Timer4とTimer5を別々に使う16bitタイマーですが、両方を合わせた32bitタイマーとしての使い方もできるため、
Timer4とTimer5の初期化処理を一つにまとめて作るようにしました。
32bitタイマーの場合は、_TIMER_5_VECTORと同じ番号のハンドラが使われます。
Timer4であれば、T4CON 、TMR4、IPC4、IFS0などが、専用のレジスタ(SFR)です。 T4CONはクロックに対する設定です。 「T4CON =0x00000000;//typeB,16bit, [1:1]プリスケール」の設定を上記でしていますが、 「T4CON =0x00000070;//typeB,16bit, [1:256]プリスケール」の設定で256分周したクロック元を指定することになります。 実は、プリスケール分周を指定しているのは、b6-b4の3bitです。 xc.hの中に様々な構造体やビットフィールドが定義されており、[1:256]プリスケールであれば次の表現ができます。 ((__T4CONbits_t *)& T4CON)->TCKPS = 3;// タイマ入力クロック [1:1]プリスケール値(b6-4) これは次のマクロを使った方が簡単でしょう。 T4CONbits.TCKPS = 3;// タイマ入力クロック [1:1]プリスケール値(b6-4) 各ビットを個別設定すると、次のようになります。 T4CONbits.ON = 0;// typeBでON(b15) T4CONbits.SIDL = 0;// デバイスがアイドルモードに移行しても動作を継続する(b13) T4CONbits.TGATE = 0;// ゲート時間積算を無効にする(b7) T4CONbits.TCKPS = 3;// タイマ入力クロック0〜7 [1:256]プリスケール値(b6-b4) T4CONbits.T32 = 0;//TMRxとTMRyは別々の16ビットタイマを構成する(b3)Timer2、4だけ使え、奇数タイマーでは無効 (1:32bit Timerで使う) T4CONbits.TCS = 0;//内部の周辺モジュール用クロック(b1) 以上で、「UMEHOSHI ITA」であれば 周辺モジュール用クロックが 40e6Hzでこれを256分周したクロックを使うことになります。 「T4CON =0x00000000;//typeB,16bit, [1:1]プリスケール」の設定で、40e6Hzクロックを使うことになります。 このクロックをTMR4でカウントアップして、PR4の値に達したら割り込み処理となります。 (PR4=2000であれば(1/40e6)/2000=0.05ミリ秒が割込み周期です) 上記割込みで次の表現があります。 IPC5bits.T5IP = 7; // Set priority level = 7(最大) IPC5bits.T5IS = 1;// Set sub-priority level = 1(最小値) IFS0bits.T5IF = 0;// Clear the timer interrupt status flag IEC0bits.T5IE = 1;// Timer5 Enable(割込み許可) //T5CONbits.ON = 1;// Start timer5 これは、次のように書くこともできます。 IPC5SET = 7 << _IPC5_T5IP_POSITION; // Set priority level = 7 IPC5SET = 1 << _IPC5_T5IS_POSITION; // Set sub-priority level = 1 IFS0CLR = _IFS0_T5IF_MASK; // Clear the timer interrupt status flag IEC0SET = _IEC0_T5IE_MASK;// Timer5 Enable(割込み許可) //T5CONSET = _T5CON_TON_MASK; // Start timer5