AVRマイクロコントローラーとTWIバスの例を使用した、OS外でのミューテックスの実装

一度気象ステーションを建設することに決めました。 センサーは、I2Cバスを含め、そこで異なります。 そして、通常は最初はどれだけ良い結果が期待の旗にすべてをしました。 しかし、実際のジェダイの進路は異なり、中断のためにすべてを停止することにしました。 これがhemoの始まりです。 私が遭遇した問題は、いくつかの連続したリクエストを処理していることです。 たとえば、 BMP085圧力センサーをさらに使用するには、EEPROMから11個のキャリブレーション定数を取得する必要があります。









決定に至った経緯と一連の考えを以下に概説します。



待機フラグ

関数get_AC1、get_AC2、...、get_MDを定義します。 それらはそれぞれ、I2Cバスを介してEEPROMセンサーから対応する定数を受け取ります。 データシートからの小包形式:









get_AC1関数のサンプルコード:



get_AC1
int get_AC1(void) { union { unsigned int Word; unsigned char Byte[2]; } AC1; //  AC1 // ---------------------------------------- TWCR = (1<<TWEN)| //  TWI  (0<<TWIE)|(1<<TWINT)| //   (0<<TWEA)|(1<<TWSTA)|(0<<TWSTO)| //  START  (0<<TWWC); while (!(TWCR & (1<<TWINT))) //    TWINT ; // ---------------------------------------- TWDR = 0xEE; //  SLA+W TWCR = (1<<TWEN)| //  TWI  (0<<TWIE)|(1<<TWINT)| //   (0<<TWEA)|(0<<TWSTA)|(0<<TWSTO)| //  SLA+W (0<<TWWC); while (!(TWCR & (1<<TWINT))) //    TWINT ; // ---------------------------------------- TWDR = 0xAA; //  0xAA TWCR = (1<<TWEN)| //  TWI  (0<<TWIE)|(1<<TWINT)| //   (0<<TWEA)|(0<<TWSTA)|(0<<TWSTO)| //  0xAA (0<<TWWC); while (!(TWCR & (1<<TWINT))) //    TWINT ; // ---------------------------------------- TWCR = (1<<TWEN)| //  TWI  (0<<TWIE)|(1<<TWINT)| //   (0<<TWEA)|(1<<TWSTA)|(0<<TWSTO)| //  REPEATED START  (0<<TWWC); while (!(TWCR & (1<<TWINT))) //    TWINT ; // ---------------------------------------- TWDR = 0xEF; //  SLA+R TWCR = (1<<TWEN)| //  TWI  (0<<TWIE)|(1<<TWINT)| //   (0<<TWEA)|(0<<TWSTA)|(0<<TWSTO)| //  SLA+R (0<<TWWC); while (!(TWCR & (1<<TWINT))) //    TWINT ; // ---------------------------------------- TWCR = (1<<TWEN)| //  TWI  (0<<TWIE)|(1<<TWINT)| //   (1<<TWEA)|(0<<TWSTA)|(0<<TWSTO)| //       ACK (0<<TWWC); while (!(TWCR & (1<<TWINT))) //    TWINT ; // ---------------------------------------- AC1.Byte[1] = TWDR; //  MSB TWCR = (1<<TWEN)| //  TWI  (0<<TWIE)|(1<<TWINT)| //   (0<<TWEA)|(0<<TWSTA)|(0<<TWSTO)| //       NACK (0<<TWWC); while (!(TWCR & (1<<TWINT))) //    TWINT ; // ---------------------------------------- AC1.Byte[0] = TWDR; //  LSB TWCR = (1<<TWEN)| //  TWI  (0<<TWIE)|(1<<TWINT)| //   (0<<TWEA)|(0<<TWSTA)|(1<<TWSTO)| //  STOP  (0<<TWWC); // ---------------------------------------- return AC1.Word; }
      
      





そして、彼女の波形は次のとおりです。









AC1の共用体の使用は、バイト順の違いによるものです。 TWIではビッグエンディアン、AVRではリトルエンディアンです。 get_AC2関数は、 SLA + Wコマンドの後に0xAAではなくバイト0xAC (データシートを参照)を送信する点のみが異なります。 他のすべての点で、機能はまったく同じです。 したがって、1つのget_Dataを定義できます。これは、データシートに従って、パラメーターとして同じレジスタアドレスを受け入れます。



get_Data
 int get_Data(unsigned char Register_adress) { union { unsigned int Word; unsigned char Byte[2]; } Data; //  Data // ---------------------------------------- TWCR = (1<<TWEN)| //  TWI  (0<<TWIE)|(1<<TWINT)| //   (0<<TWEA)|(1<<TWSTA)|(0<<TWSTO)| //  START  (0<<TWWC); while (!(TWCR & (1<<TWINT))) //    TWINT ; // ---------------------------------------- TWDR = 0xEE; //  SLA+W TWCR = (1<<TWEN)| //  TWI  (0<<TWIE)|(1<<TWINT)| //   (0<<TWEA)|(0<<TWSTA)|(0<<TWSTO)| //  SLA+W (0<<TWWC); while (!(TWCR & (1<<TWINT))) //    TWINT ; // ---------------------------------------- TWDR = Register_adress; //  Register_adress TWCR = (1<<TWEN)| //  TWI  (0<<TWIE)|(1<<TWINT)| //   (0<<TWEA)|(0<<TWSTA)|(0<<TWSTO)| //  Register_adress (0<<TWWC); while (!(TWCR & (1<<TWINT))) //    TWINT ; // ---------------------------------------- TWCR = (1<<TWEN)| //  TWI  (0<<TWIE)|(1<<TWINT)| //   (0<<TWEA)|(1<<TWSTA)|(0<<TWSTO)| //  REPEATED START  (0<<TWWC); while (!(TWCR & (1<<TWINT))) //    TWINT ; // ---------------------------------------- TWDR = 0xEF; //  SLA+R TWCR = (1<<TWEN)| //  TWI  (0<<TWIE)|(1<<TWINT)| //   (0<<TWEA)|(0<<TWSTA)|(0<<TWSTO)| //  SLA+R (0<<TWWC); while (!(TWCR & (1<<TWINT))) //    TWINT ; // ---------------------------------------- TWCR = (1<<TWEN)| //  TWI  (0<<TWIE)|(1<<TWINT)| //   (1<<TWEA)|(0<<TWSTA)|(0<<TWSTO)| //       ACK (0<<TWWC); while (!(TWCR & (1<<TWINT))) //    TWINT ; // ---------------------------------------- Data.Byte[1] = TWDR; //  MSB TWCR = (1<<TWEN)| //  TWI  (0<<TWIE)|(1<<TWINT)| //   (0<<TWEA)|(0<<TWSTA)|(0<<TWSTO)| //       NACK (0<<TWWC); while (!(TWCR & (1<<TWINT))) //    TWINT ; // ---------------------------------------- Data.Byte[0] = TWDR; //  LSB TWCR = (1<<TWEN)| //  TWI  (0<<TWIE)|(1<<TWINT)| //   (0<<TWEA)|(0<<TWSTA)|(1<<TWSTO)| //  STOP  (0<<TWWC); // ---------------------------------------- return Data.Word; }
      
      





そして、ちょうど:



 struct { short AC1; short AC2; short AC3; unsigned short AC4; unsigned short AC5; unsigned short AC6; short B1; short B2; short MB; short MC; short MD; } BMP085_EEPROM; ... static void get_EEPROM(void) { BMP085_EEPROM.AC1 = get_Data(0xAA); BMP085_EEPROM.AC2 = get_Data(0xAC); BMP085_EEPROM.AC3 = get_Data(0xAE); BMP085_EEPROM.AC4 = get_Data(0xB0); BMP085_EEPROM.AC5 = get_Data(0xB2); BMP085_EEPROM.AC6 = get_Data(0xB4); BMP085_EEPROM.B1 = get_Data(0xB6); BMP085_EEPROM.B2 = get_Data(0xB8); BMP085_EEPROM.MB = get_Data(0xBA); BMP085_EEPROM.MC = get_Data(0xBC); BMP085_EEPROM.MD = get_Data(0xBE); }
      
      





結果は次のとおりです。









中断作業



まえがき








上の図からわかるように、1つの動作の継続時間は、バス転送周波数が100 kHzの場合、 488μsであり、これは8 MHz3094プロセッサクロック速度です 。 まあ、これはみんなじゃない。 もちろん、ターゲットデバイスで許可されている場合は、頻度を増やすことができます。 たとえば、 400 kHzの場合、持続時間は128μsまたは1024サイクルです









フラグが立ち上がるのを待つことは、まったく役に立ちません。 プロセッサは数千クロックサイクルにわたって、たとえば浮動小数点数を除算する操作を実行するなど、多くの有用な作業を実行できます。 したがって、この状況から抜け出す唯一の適切な方法は、割り込みを使用することです。



中断作業


構造を定義します。

 struct { unsigned char SLA; // Slave address unsigned char *pW; // ? unsigned char nW; // ? unsigned char *pR; // ? unsigned char nR; // ? } TWI;
      
      





そして今、 get_AC1関数:

 unsigned char Register_address; ... void get_AC1(void) { Register_address = 0xAA; //  Register address TWI.SLA = 0x77; // Slave address  BMP085 TWI.pW = &Register_address; //       TWI.nW = 1; //   ? TWI.pR = &BMP085_EEPROM.AC1; //   ? TWI.nR = 2; //   ? TWCR = (1<<TWEN)| //  TWI  (1<<TWIE)|(1<<TWINT)| //  !   (0<<TWEA)|(1<<TWSTA)|(0<<TWSTO)| //  START  (0<<TWWC); }
      
      





割り込みハンドラーについて一言。 nW = nnR = 0の場合、フレーム形式は次のとおりです。







nW = 0の場合、 nR = m







nW = nの場合nR = m







ところで、割り込みハンドラは何でも構いません。 ステートマシンロジックはさまざまな方法で実装できます。 私の例を以下に示します。

割り込みハンドラー
 ISR(TWI_vect) { /* ----------------------------------------------------------------------------------- Jump table which is stored in flash ------------------------------------------------------------------------------------*/ static const void * const twi_list[] PROGMEM = {&&TWI_00, &&TWI_08, &&TWI_10, &&TWI_18, &&TWI_20, &&TWI_28, &&TWI_30, &&TWI_38, &&TWI_40, &&TWI_48, &&TWI_50, &&TWI_58, &&TWI_60, &&TWI_68, &&TWI_70, &&TWI_78, &&TWI_80, &&TWI_88, &&TWI_90, &&TWI_98, &&TWI_A0, &&TWI_A8, &&TWI_B0, &&TWI_B8, &&TWI_C0, &&TWI_C8, &&TWI_F8}; /* ----------------------------------------------------------------------------------- Jump to label, address of which is in twi_list[TWSR>>3] ------------------------------------------------------------------------------------*/ goto *(pgm_read_word(&(twi_list[TWSR>>3]))); /* ----------------------------------------------------------------------------------- Bus error handler ------------------------------------------------------------------------------------*/ TWI_00: // STOP condition will be generated goto STOP; /* ----------------------------------------------------------------------------------- A START condition has been transmitted A repeated START condition has been transmitted nW = nR = 0: STOP condition will be generated nW > 0, nR - don't care: SLA+W will be send nW = 0, nR > 0: SLA+R will be send ------------------------------------------------------------------------------------*/ TWI_08: TWI_10: if (TWI.nW != 0) // SLA+W will be send TWDR = (TWI.SLA)<<1; else if (TWI.nR != 0) // SLA+R will be send TWDR = (TWI.SLA)<<1 | 1<<0; else // STOP condition will be generated goto STOP; TWCR = (1<<TWEN)| //  TWI  (1<<TWIE)|(1<<TWINT)| //  !   (0<<TWEA)|(0<<TWSTA)|(0<<TWSTO)| //  SLA+R/W (0<<TWWC); return; /* ----------------------------------------------------------------------------------- SLA+W has been transmitted; ACK has been received Data byte has been transmitted; ACK has been received nW > 0, nR - don't care: Data byte will be transmitted and ACK or NOT ACK will be received nW = 0, nR > 0: Repeated START will be transmitted nW = nR = 0: STOP condition will be generated ------------------------------------------------------------------------------------*/ TWI_18: TWI_28: if (TWI.nW != 0) { // Data byte will be transmitted and ACK or NOT ACK will be received TWDR = *TWI.pW++; TWCR = (1<<TWEN)| //  TWI  (1<<TWIE)|(1<<TWINT)| //  !   (0<<TWEA)|(0<<TWSTA)|(0<<TWSTO)| //   (0<<TWWC); TWI.nW--; } else if (TWI.nR != 0) // Repeated START will be transmitted TWCR = (1<<TWEN)| //  TWI  (1<<TWIE)|(1<<TWINT)| //  !   (0<<TWEA)|(1<<TWSTA)|(0<<TWSTO)| //  START  (0<<TWWC); else // STOP condition will be generated goto STOP; return; /* ----------------------------------------------------------------------------------- SLA+W has been transmitted; NOT ACK has been received ------------------------------------------------------------------------------------*/ TWI_20: // STOP condition will be generated goto STOP; /* ----------------------------------------------------------------------------------- Data byte has been transmitted; NOT ACK has been received ------------------------------------------------------------------------------------*/ TWI_30: // STOP condition will be generated goto STOP; /* ----------------------------------------------------------------------------------- Arbitration lost in SLA+W or data bytes Arbitration lost in SLA+R or NOT ACK bit ------------------------------------------------------------------------------------*/ TWI_38: // STOP condition will be generated goto STOP; /* ----------------------------------------------------------------------------------- SLA+R has been transmitted; ACK has been received nR = 1: Data byte will be received and NOT ACK will be returned nR > 1: Data byte will be received and ACK will be returned ------------------------------------------------------------------------------------*/ TWI_40: if (TWI.nR == 1) // Data byte will be received and NOT ACK will be returned TWCR = (1<<TWEN)| //  TWI  (1<<TWIE)|(1<<TWINT)| //  !   (0<<TWEA)|(0<<TWSTA)|(0<<TWSTO)| //    + NACK (0<<TWWC); else // Data byte will be received and ACK will be returned TWCR = (1<<TWEN)| //  TWI  (1<<TWIE)|(1<<TWINT)| //  !   (1<<TWEA)|(0<<TWSTA)|(0<<TWSTO)| //    + ACK (0<<TWWC); return; /* ----------------------------------------------------------------------------------- SLA+R has been transmitted; NOT ACK has been received ------------------------------------------------------------------------------------*/ TWI_48: // STOP condition will be generated goto STOP; /* ----------------------------------------------------------------------------------- Data byte has been received; ACK has been returned nR = 2: Data byte will be received and NOT ACK will be returned nR > 2: Data byte will be received and ACK will be returned ------------------------------------------------------------------------------------*/ TWI_50: // Read data *TWI.pR++ = TWDR; if (TWI.nR-- == 2) // Data byte will be received and NOT ACK will be returned TWCR = (1<<TWEN)| //  TWI  (1<<TWIE)|(1<<TWINT)| //  !   (0<<TWEA)|(0<<TWSTA)|(0<<TWSTO)| //    + NACK (0<<TWWC); else // Data byte will be received and ACK will be returned TWCR = (1<<TWEN)| //  TWI  (1<<TWIE)|(1<<TWINT)| //  !   (1<<TWEA)|(0<<TWSTA)|(0<<TWSTO)| //    + ACK (0<<TWWC); return; /* ----------------------------------------------------------------------------------- Data byte has been received; NOT ACK has been returned Repeated START will be transmitted STOP condition will be transmitted and TWSTO Flag will be reset STOP condition followed by a START condition will be transmitted and TWSTO Flag will be reset ------------------------------------------------------------------------------------*/ TWI_58: // Read data *TWI.pR = TWDR; TWI_60: TWI_68: TWI_70: TWI_78: TWI_80: TWI_88: TWI_90: TWI_98: TWI_A0: TWI_A8: TWI_B0: TWI_B8: TWI_C0: TWI_C8: TWI_F8: // STOP condition will be transmitted and TWSTO Flag will be reset STOP: TWCR = (1<<TWEN)| //  TWI  (1<<TWIE)|(1<<TWINT)| //  !   (0<<TWEA)|(0<<TWSTA)|(1<<TWSTO)| //    + NACK (0<<TWWC); }
      
      





さて、仕事の結果:









ご覧のとおり、ランタイムは488μsから538μsに増加しました。 これは、ハンドラへの遷移とハンドラからの戻り、およびルックアップテーブルのジャンプアドレスの計算によるものです。 しかし、最も重要なことは、伝送全体がハードウェアで行われることです。 したがって、 3.5μsまたは28クロックサイクルしか続かない小さなget_AC1関数を実行した後、他のことを安全に行うことができ、待機サイクルでスタックすることはありません。









get_AC1get_AC2 、...、 get_MD関数を順番に呼び出すとどうなるか見てみましょう。









get_MD実行するのは1つだけで、すべての理由は次のとおりです。









最後の関数は、SLA + Wが完了する前に構造にデータをロードします。したがって、Register_addressはそれに対応し、 0xBEと等しくなります。 実際、これはおそらく最も無害なシナリオです。 結局、転送がRegister_addressよりも少し速い場合、たとえばget_AC5関数に対応し、 BMP085_EEPROM.MDにそれをまったく書き込んだでしょう。 つまり、 AC1を期待し、 MDに AC5を保存します 。 また、 get_AC5の代わりに、独自のSLAを持つ別のアドレスがある場合、一方のアドレスの半分と他方のアドレスを送信してNACKを取得すると、割り込みハンドラーからのロジックはプログラムをループするか、STOPをスローします。 この状況から抜け出すための明確な方法が1つあります。 前の機能が終了するまで、次の機能を実行しないでください。 つまり、 get_AC1を実行することでフラグを1に設定し、STOPを完了したハンドラーはそれをリセットします。 また、このフラグがクリアされるまでget_AC2は開始されません。 はい、これは解決方法ですが、中断の魅力はすべて失われます。この場合、0%も使用されます。 より良いフラッグマシン。 しかし、別の美しいソリューションがあります。



ミューテックスアナログ


get_AC1 、...、 get_MD関数はデータを直接送信しません。 それらはキューにそれらを置く別の関数を呼び出し、キューが空でない場合、それ自体が転送の開始を初期化します。 構造体の配列を作成して、構造体を書き換えます。

 #define size 8 ... typedef struct { unsigned char SLA; // Slave address unsigned char *pW; // ? unsigned char nW; // ? unsigned char *pR; // ? unsigned char nR; // ? } twi; twi TWI[size];
      
      







get_AC1関数どのように見えるかを見てみましょう。

 unsigned char buf[size]; ... void get_AC1(void) { volatile twi *pl; //     twi buf[0] = 0xAA; //  Register address pl->SLA = 0x77; // Slave address  BMP085 pl->pW = buf; //       pl->nW = 1; //   ? pl->pR = buf; //   ? pl->nR = 2; //   ? Scheduler(pl); //    }
      
      





実際には、START状態を初期化する代わりに、TWIフレームに必要なすべてのデータを含むtwi- type 構造体へのポインターを渡すScheduler関数を呼び出します。 スケジューラーの基本機能とハンドラーへのいくつかの変更を考慮してください。



上記の内容を考慮する必要はありません。 上記のいくつかのポイントを明確にする小さなプレゼンテーションを見るのが良いでしょう:





スケジューラースケジューラー機能

 void Scheduler(twi *pl) { if (tail-head !=1 && head-tail != size-1) //    ,  { twi *pg = &TWI[head]; //      pg->SLA = pl->SLA; //  SLA pg->pW = pl->pW; //  *pW pg->nW = pl->nW; //  nW pg->pR = pl->pR; //  *pR pg->nR = pl->nR; //  nR head = (head+1)&(size-1); //       -  if (!flag.twi_run) //   TWI    (  0) { flag.twi_run = 1; //    1 TWCR = (1<<TWEN)| //  TWI  (1<<TWIE)|(1<<TWINT)| (0<<TWEA)|(1<<TWSTA)|(0<<TWSTO)| (0<<TWWC); } } }
      
      





さて、ハンドラー自体:

再設計された割り込みハンドラー
 ISR(TWI_vect) { twi *p = &TWI[tail]; /* ----------------------------------------------------------------------------------- Jump table which is stored in flash ------------------------------------------------------------------------------------*/ static const void * const twi_list[] PROGMEM = {&&TWI_00, &&TWI_08, &&TWI_10, &&TWI_18, &&TWI_20, &&TWI_28, &&TWI_30, &&TWI_38, &&TWI_40, &&TWI_48, &&TWI_50, &&TWI_58, &&TWI_60, &&TWI_68, &&TWI_70, &&TWI_78, &&TWI_80, &&TWI_88, &&TWI_90, &&TWI_98, &&TWI_A0, &&TWI_A8, &&TWI_B0, &&TWI_B8, &&TWI_C0, &&TWI_C8, &&TWI_F8}; /* ----------------------------------------------------------------------------------- Jump to label, address of which is in twi_list[TWSR>>3] ------------------------------------------------------------------------------------*/ goto *(pgm_read_word(&(twi_list[TWSR>>3]))); /* ----------------------------------------------------------------------------------- Bus error handler ------------------------------------------------------------------------------------*/ TWI_00: // STOP condition will be generated goto STOP; /* ----------------------------------------------------------------------------------- A START condition has been transmitted A repeated START condition has been transmitted nW = nR = 0: STOP condition will be generated nW > 0, nR - don't care: SLA+W will be send nW = 0, nR > 0: SLA+R will be send ------------------------------------------------------------------------------------*/ TWI_08: TWI_10: if (p->nW != 0) // SLA+W will be send TWDR = p->SLA<<1; else if (p->nR != 0) // SLA+R will be send TWDR = p->SLA<<1 | 1<<0; else // STOP condition will be generated goto STOP; TWCR = (1<<TWEN)| //  TWI  (1<<TWIE)|(1<<TWINT)| //  !   (0<<TWEA)|(0<<TWSTA)|(0<<TWSTO)| //  SLA+R/W (0<<TWWC); return; /* ----------------------------------------------------------------------------------- SLA+W has been transmitted; ACK has been received Data byte has been transmitted; ACK has been received nW > 0, nR - don't care: Data byte will be transmitted and ACK or NOT ACK will be received nW = 0, nR > 0: Repeated START will be transmitted nW = nR = 0: STOP condition will be generated ------------------------------------------------------------------------------------*/ TWI_18: TWI_28: if (p->nW != 0) { // Data byte will be transmitted and ACK or NOT ACK will be received TWDR = *p->pW; p->pW++; TWCR = (1<<TWEN)| //  TWI  (1<<TWIE)|(1<<TWINT)| //  !   (0<<TWEA)|(0<<TWSTA)|(0<<TWSTO)| //   (0<<TWWC); p->nW--; } else if (p->nR != 0) // Repeated START will be transmitted TWCR = (1<<TWEN)| //  TWI  (1<<TWIE)|(1<<TWINT)| //  !   (0<<TWEA)|(1<<TWSTA)|(0<<TWSTO)| //  START  (0<<TWWC); else // STOP condition will be generated goto STOP; return; /* ----------------------------------------------------------------------------------- SLA+W has been transmitted; NOT ACK has been received ------------------------------------------------------------------------------------*/ TWI_20: // STOP condition will be generated goto STOP; /* ----------------------------------------------------------------------------------- Data byte has been transmitted; NOT ACK has been received ------------------------------------------------------------------------------------*/ TWI_30: // STOP condition will be generated goto STOP; /* ----------------------------------------------------------------------------------- Arbitration lost in SLA+W or data bytes Arbitration lost in SLA+R or NOT ACK bit ------------------------------------------------------------------------------------*/ TWI_38: // STOP condition will be generated goto STOP; /* ----------------------------------------------------------------------------------- SLA+R has been transmitted; ACK has been received nR = 1: Data byte will be received and NOT ACK will be returned nR > 1: Data byte will be received and ACK will be returned ------------------------------------------------------------------------------------*/ TWI_40: if (p->nR == 1) // Data byte will be received and NOT ACK will be returned TWCR = (1<<TWEN)| //  TWI  (1<<TWIE)|(1<<TWINT)| //  !   (0<<TWEA)|(0<<TWSTA)|(0<<TWSTO)| //    + NACK (0<<TWWC); else // Data byte will be received and ACK will be returned TWCR = (1<<TWEN)| //  TWI  (1<<TWIE)|(1<<TWINT)| //  !   (1<<TWEA)|(0<<TWSTA)|(0<<TWSTO)| //    + ACK (0<<TWWC); return; /* ----------------------------------------------------------------------------------- SLA+R has been transmitted; NOT ACK has been received ------------------------------------------------------------------------------------*/ TWI_48: // STOP condition will be generated goto STOP; /* ----------------------------------------------------------------------------------- Data byte has been received; ACK has been returned nR = 2: Data byte will be received and NOT ACK will be returned nR > 2: Data byte will be received and ACK will be returned ------------------------------------------------------------------------------------*/ TWI_50: // Read data *p->pR = TWDR; p->pR++; if (p->nR-- == 2) // Data byte will be received and NOT ACK will be returned TWCR = (1<<TWEN)| //  TWI  (1<<TWIE)|(1<<TWINT)| //  !   (0<<TWEA)|(0<<TWSTA)|(0<<TWSTO)| //    + NACK (0<<TWWC); else // Data byte will be received and ACK will be returned TWCR = (1<<TWEN)| //  TWI  (1<<TWIE)|(1<<TWINT)| //  !   (1<<TWEA)|(0<<TWSTA)|(0<<TWSTO)| //    + ACK (0<<TWWC); return; /* ----------------------------------------------------------------------------------- Data byte has been received; NOT ACK has been returned Repeated START will be transmitted STOP condition will be transmitted and TWSTO Flag will be reset STOP condition followed by a START condition will be transmitted and TWSTO Flag will be reset ------------------------------------------------------------------------------------*/ TWI_58: // Read data *p->pR = TWDR; TWI_60: TWI_68: TWI_70: TWI_78: TWI_80: TWI_88: TWI_90: TWI_98: TWI_A0: TWI_A8: TWI_B0: TWI_B8: TWI_C0: TWI_C8: TWI_F8: // STOP condition will be transmitted and TWSTO Flag will be reset STOP: tail = (tail+1)&(size-1); //  tail  1,    ,   if (head != tail) //  head  tail  ,  TWCR = (1<<TWEN)| //  TWI  (1<<TWIE)|(1<<TWINT)| //  !   (0<<TWEA)|(1<<TWSTA)|(1<<TWSTO)| // - (0<<TWWC); else //  { TWCR = (1<<TWEN)| //  TWI  (1<<TWIE)|(1<<TWINT)| //  !   (0<<TWEA)|(0<<TWSTA)|(1<<TWSTO)| //  (0<<TWWC); flag.twi_run = 0; //   } }
      
      





さて、 get_AC1の結果:









あとがき


実際、 get_AC1と同様に、 get_AC2を作成し、2つの関数を連続して実行すると、最後の関数のみが2回実行されます。 これは、送信用のデータをbuf [0]に常に保存しているためです。 これを避けるために、buf [1]にデータを書き込むことができ、すべてが正常に機能します。 しかし、これは行われていません。 データを取得する場所と量からポインターを転送するのは正しいことです。一部のサードパーティ関数は、これをTWIのバッファーに入れ、ハンドラーでデータを取得する場所からポインターを返します。 一般に、たとえば次のコード:



コード
 unsigned char bufTx[size]; unsigned char pos = 0; ... void get_AC1(void) { volatile twi *pl; //     twi volatile uint8_t buf[] = {0xAA}; //  Register address  buf pl->SLA = 0x77; // Slave address  BMP085 pl->pW = buf; //       pl->nW = 1; //   ? pl->pR = buf; //   ? pl->nR = 2; //   ? Scheduler(pl); //    } // ============================================ void get_AC2(void) { volatile twi *pl; //     twi volatile uint8_t buf[] = {0xAC}; //  Register address  buf pl->SLA = 0x77; // Slave address  BMP085 pl->pW = buf; //       pl->nW = 1; //   ? pl->pR = buf; //   ? pl->nR = 2; //   ? Scheduler(pl); //    } // ============================================ void Scheduler(volatile twi *pl) { if (tail-head !=1 && head-tail != size-1) //    ,  { twi *pg = &TWI[head]; //      pg->SLA = pl->SLA; //  SLA pg->pW = pushToBuf(pl->pW,pl->nW); //  ,    //  buf  bufTX    pg->nW = pl->nW; //  nW pg->pR = pl->pR; //  *pR pg->nR = pl->nR; //  nR head = (head+1)&(size-1); //       -  if (!flag.twi_run) //   TWI    (  0) { flag.twi_run = 1; //    1 TWCR = (1<<TWEN)| //  TWI  (1<<TWIE)|(1<<TWINT)| (0<<TWEA)|(1<<TWSTA)|(0<<TWSTO)| (0<<TWWC); } } } // ============================================ unsigned char *pushToBuf(unsigned char *buf, unsigned char n) { unsigned char *p = &bufTx[pos]; //      do { bufTx[pos++] = *buf++; //   buf  bufTx pos &= size-1; //   -  } while (--n); //  n  return p; //   p }
      
      





写真を見てください:









類推により、受信バッファに対して同じ機能を実行する必要があります。あなたは彼女にいくら教えて、彼女はあなたをどこに送ります。TWIの構造を変更することもできます。たとえば、ポインターではなくインデックスを送信して、メモリを最適化します。

リセットにコードが使用される理由について質問がある場合:

 head = (head+1)&(size-1); tail = (tail+1)&(size-1); pos &= size-1;
      
      





私はこれをより速く、より少ないコードと言います。唯一の条件は、値が2 nであることです。

機能のうち、TWIの最大サイズサイズに注意する価値もあります。連続したリクエストの最大可能数よりも少ない場合、それらの一部が確実に失われます。したがって、これを強調することが重要です。同時要求の最大数より少なくとも1大きいサイズ値を常に使用してください

関数を実行した後、受信したデータのハンドラーを追加することが可能です。この例では、これは受信したAC1とAC2の処理であり、ビッグエンディアンで受信し、リトルエンディアンに変換してから適切な場所に保存する必要があります。これを行うには、TWI構造で、関数ハンドラーへのポインターを保存し、ワークアウト後に呼び出す方法を見つけます。

プロセスについて言及する価値のある言葉。私のスキームに関連して、TWIバスには2つの混乱、圧力、1つのタイムチップ、1つのメモリチップのセンサーがあります。そして、それらへのアクセスは、たとえば圧力センサーの場合、アトミックと見なすことができます。









非圧縮圧力で伝送が失われた場合、取得した非圧縮温度の値は不要であり、無効なデータを取得できるため、最初からやり直す必要があります。

また、TWIを介して送信するときに障害が発生することはありません。ハンドラーで元のTWI [i]を変更しているため、転送を再開できません。これを行うには、twi型のグローバル構造を宣言し、各開始前に、TWI [tail]からデータをコピーして、プロセッサにコピーを台無しにさせます。障害が発生した場合、元のデータを復元できます。



おわりに



多くの人が尋ねます:「なぜ?実際、構造の16個の要素を保存するには、16 *(1 + 2 + 1 + 2 + 1)= 112バイトのSRAMが必要です!RTOSを使用しないのはなぜですか?1つの要素を持つ構造があり、それに収まらない場合は、nミリ秒待機します。」このようなソリューションは、オペレーティングシステムに非常に役立つと思います。この転送にかかる時間について考えてください。キューに入れると、次の各送信はnミリ秒になり、全体はn * mミリ秒で終了します。そして、本当に重要なタスクを見逃す可能性があるため、不必要なm命令でキューイングするのはなぜですか。また、OSカーネルはアンロードされ、m *(m + 1)/ 2タスクをキューに入れません。



All Articles