1

私は組み込みデバイスのファームウェア (C で記述) に取り組んでいます。ディスプレイからスクリーンショットを撮り、bmp ファイルとして保存する必要があります。現在、bmp ファイル データを生成するモジュールに取り組んでいます。これを行う最も簡単な方法は、次の引数を取る関数を作成することです。

(簡単にするために、この例ではインデックス付きの色の画像のみがサポートされています)

  • 色深度
  • 画像サイズ(幅、高さ)
  • color_index のパレット色を取得する関数へのポインター (i)
  • 指定された座標 (x, y) を持つピクセルの color_index を取得する関数へのポインタ
  • 画像データを書き込む関数へのポインタ

そして、この関数のユーザーは次のように呼び出す必要があります。

/*
 * Assume we have the following functions:
 *    int_least32_t palette_color_get  (int color_index);
 *    int           pix_color_idx_get  (int x, int y);
 *    void          data_write         (const char *p_data, size_t len);
 */

bmp_file_generate(
      1,         //-- color_depth
      x, y,      //-- size
      palette_color_get,
      pic_color_idx_get,
      data_write
      );

それだけです: この関数はすべてのジョブを実行し、ジョブが完了したときにのみ戻ります (つまり、指定されたユーザー コールバック関数によって生成され、「書き込まれた」bmp ファイル) data_write()

bmp_writerしかし、モジュールを協調 RTOS で使用できるようにする必要がありdata_write()、実際に何らかのプロトコル (UART など) を介して別のデバイスにデータを送信する関数である可能性があるため、この関数はタスク コンテキストからのみ呼び出す必要があります。その場合、このアプローチは機能しません。オブジェクト指向スタイルで作成する必要があり、その使用法は次のようになります。

/*
 * create instance of bmp_writer with needed params
 * (we don't need "data_write" pointer anymore)
 */
T_BmpWriter *p_bmp_writer = new_bmp_writer(
      1,         //-- color_depth
      x, y,      //-- size
      palette_color_get,
      pic_color_idx_get
      );

/*
 * Now, byte-by-byte get all the data!
 */
while (bmp_writer__data_available(p_bmp_writer) > 0){
   char cur_char = bmp_writer__get_next_char(p_bmp_writer);
   //-- do something useful with current byte (i.e. cur_char).
   //   maybe transmit to another device, or save to flash, or anything.
}

/*
 * Done! Free memory now.
 */ 
delete_bmp_writer(p_bmp_writer);

ご覧のとおり、ユーザーはbmp_writer__get_next_char(p_bmp_writer)必要なときに呼び出し、受信したデータを必要に応じて処理できます。

実はこれは実装済みなのですが、そのアプローチではアルゴリズムがすべて裏返しになってしまい、このコードは非常に読みにくくなってしまいます。

パレット データを生成する古いコードの一部 (すべてのジョブを実行し、ジョブが完了したときにのみ戻る関数から) と、新しいコードの適切な部分 (ステート マシン スタイル) を示します。

古いコード:

void bmp_file_generate(/*....args....*/)
{
   //-- ... write headers

   //-- write palette (if needed)
   if (palette_colors_cnt > 0){
      size_t i;
      int_least32_t cur_color;
      for (i = 0; i < palette_colors_cnt; i++){
         cur_color = callback_palette_color_get(i);
         callback_data_write((const char *)&cur_color, sizeof(cur_color));
      }
   }

   //-- ...... write image data ..........
}

ご覧のとおり、非常に短くて読みやすいコードです。

さて、新しいコード。

実際にはステージ (HEADER_WRITE、PALETTE_WRITE、IMG_DATA_WRITE) で分割されているため、ステート マシンのように見えます。各ステージには独自のコンテキストがあります。古いコードでは、コンテキストはローカル変数に保存されていましたが、構造体を作成してヒープから割り当てる必要があります。そう:

/*
 * Palette stage context
 */
typedef struct {
   size_t         i;
   size_t         cur_color_idx;
   int_least32_t  cur_color;
} T_StageContext_Palette;

/*
 * Function that switches stage.
 * T_BmpWriter is an object context, and pointer *me is analogue of "this" in OO-languages.
 * bool_start is 1 if stage is just started, and 0 if it is finished.
 */
static void _stage_start_end(T_BmpWriter *me, U08 bool_start)
{
   switch (me->stage){
      //-- ...........other stages.........

      case BMP_WR_STAGE__PALETTE:
         if (bool_start){
            //-- palette stage is just started. Allocate stage context and initialize it.
            me->p_stage_context = malloc(sizeof(T_StageContext_Palette));
            memset(me->p_stage_context, 0x00, sizeof(T_StageContext_Palette));

            //-- we need to get first color, so, set index of byte in cur_color to maximum
            ((T_StageContext_Palette *)me->p_stage_context)->i = sizeof(int_least32_t);
         } else {
            free(me->p_stage_context);
            me->p_stage_context = NULL;
         }
         break;

      //-- ...........other stages.........

   }
}


/*
 * Function that turns to the next stage
 */
static void _next_stage(T_BmpWriter *me)
{
   _stage_start_end(me, 0);
   me->stage++;
   _stage_start_end(me, 1);
}

/*
 * Function that actually does the job and returns next byte
 */
U08 bmp_writer__get_next_char(T_BmpWriter *me)
{
   U08 ret = 0;        //-- resulting byte to return
   U08 bool_ready = 0; //-- flag if byte is ready

   while (!bool_ready){
      switch (me->stage){

         //-- ...........other stages.........

         case BMP_WR_STAGE__PALETTE:
            {
               T_StageContext_Palette *p_stage_context =
                  (T_StageContext_Palette *)me->p_stage_context;

               if (p_stage_context->i < sizeof(int_least32_t)){
                  //-- return byte of cur_color
                  ret = *( (U08 *)&p_stage_context->cur_color + p_stage_context->i );
                  p_stage_context->i++;
                  bool_ready = 1;
               } else {
                  //-- need to get next color (or even go to next stage)

                  if (p_stage_context->cur_color_idx < me->bmp_details.palette_colors_cnt){
                     //-- next color
                     p_stage_context->cur_color = me->callback.p_palette_color_get(
                           me->callback.user_data,
                           p_stage_context->cur_color_idx
                           );
                     p_stage_context->cur_color_idx++;
                     p_stage_context->i = 0;
                  } else {
                     //-- next stage!
                     _next_stage(me);
                  }
               }

            }
            break;

         //-- ...........other stages.........

      }
   }

   return ret;

}

とても巨大なコードで、理解するのはとても難しいです! しかし、バイト単位で情報を取得できるようにするために、別の方法でそれを作成する方法が本当にわかりません。

これを達成し、コードの可読性を維持する方法を知っている人はいますか?

どんな助けでも大歓迎です。

4

1 に答える 1

4

ステートマシンベースのプログラムをスレッドスタイルのプログラムに変換するのに便利なprotothreadを試すことができます。それがあなたの問題をエレガントに解決できるかどうか100%確信していません、あなたはそれを試してみることができます。紙は良い出発点です:Protothreads: simplifying event-driven programming of memory-constrained embedded systems

ソースコードは次のとおりです:http ://code.google.com/p/protothread/

ちなみに、ProtothreadはContiki組み込みOSでも使用され、Contikiでプロセスを実装します。

于 2012-12-05T07:55:09.023 に答える