« 2010年10月 | トップページ | 2010年12月 »

2010年11月の記事

mbedでリニア温度センサーを使う

MTM06で@shintamainjpさんよりStarBord Orange購入記念のMPC9700(アナログ温度センサー)をいただきました。mbedのADCを使ってセンサーの読み取りと温度の表示を行うサンプルプログラムを作って、本家のmbed.orgのNotebookにポストしたのですが、自分のブログにも概要を記載します。

   
温度センサーの読み取り 

MCP9700等のアナログ温度センサーは、温度に比例して出力電圧が変化します(温度係数と呼びます)。そのため、出力電圧を測定することによって、温度を得ることができます。MPC9700の場合、1℃毎に10mV出力が変化します。また、0℃で500mVの出力があるため、出力電圧は以下の式で表せます: 

Vout(出力電圧) = Tc(温度係数) x Ta(温度)  + V0(0℃の出力) 

そのため、温度は以下の式で計算が可能です: 

Ta = (Vout - V0) / Tc = (Vout - 500) / 10 

上記の式を使用して数秒単位で温度を測定すると、測定値に結構なばらつきが出てしまいました(1℃の単位で表示がふらついて、うっとうしい状態になります)。そのため、10回サンプル(測定)を行った平均値を示すようにしてあります。この対策を行うと、温度表示のばたつきはかなり軽減されるのですが、小数点まで表示すると0.1℃の単位は若干ばたつきます。 

センサーの出力電圧をテスターで直接計ると、ばたつきは1mVの単位しかなくADCの読み取り値に比べて安定しています。そのため、測定値のばらつきはセンサー入力の処理にも問題がありそうです。センサーの出力が20℃で700mVと低めのため、OPアンプを入れて出力を2倍程度に増幅してやればADCの分解能を生かせるようになり、ばたつきが軽減できるかも。あとノイズ対策も必要か。 

   
サンプルプログラム 

センサーの読み取りは、あまり意味がないですが、mbedのオブジェクト風に書いてみました。コード全体は、mbed.orgを参照して下さい。センサーオブジェクトを使ったサンプルを以下に示します。

  
#include "mbed.h"
#include "TextLCD.h"
#include "LinearTempSensor.h"

TextLCD lcd(p24, p26, p27, p28, p29, p30, TextLCD::LCD16x2);  // RS, E, DB4, DB5, DB6, DB7
LinearTempSensor sensor(p20);                                 // With default parameters
//LinearTempSensor sensor(p20, 5, LinearTempSensor::MCP9700); // With option parameters

int main() 
{
    float Vout, Tav, To;

    lcd.cls();
    lcd.printf("TEMP:");
    
    while(true)
    {
        Vout = sensor.Sense();          // Sample data (read sensor)
        Tav  = sensor.GetAverageTemp(); // Calculate average temperature from N samples
        To   = sensor.GetLatestTemp();  // Calculate temperature from the latest sample

        lcd.locate(5, 0);
        lcd.printf("%4.1f", Tav);
        printf("Vout:%f  Tav:%f  To:%f\n\r", Vout, Tav, To);    // Debug print

        wait(2.0);
    }
}

6行目で、AnalogInとしてp20を使用する、LinearTempSensorオブジェクトのインスタンスを起こしています。  
18行目でセンサー出力をサンプリングし、19行目でN回サンプル(defaultは10回)から得られる平均温度を取得しています。

StarBoard Orangeを使ってセンサーを動作させた写真を以下に示します。

SensorWithMbed

mbedでDMAの実験

8-bitマイコンのArduinoでは味わえない32-bitマイコンの醍醐味(?)としてDMAがあります。NXP提供のCMSIS DMAライブラリのデモコードをmbedに移植して実験をしてみました。


実験の内容

  1. SRAM→SRAM,  Flash ROM→SRAM間で32 wordのDMAを起動
  2. DMA起動後、メインルーチンはループカウンタのインクリメントを行うだけの無限ループに入る。
  3. DMA完了割り込みルーチン内で再度DMAを起動することで、DMAによるメモリコピーとループが平行して動く(筈)
  4. DMAが1000回動いた時点でプログラムを止めて、起動からの経過時間とループカウンタ値を表示
  5. DMAのソースがSRAMとFlash ROMの場合の差分をチェック

ポイントは5項でありまして、図に示す通り、①のSRAM→SRAM間DMAでは、DMAと命令フェッチでメモリアクセスが競合しないと考えられます。一方、②のROM→SRAMのDMAではROMアクセスが競合するためループ処理が①に比べてまわらないというのが期待値です。

LPC1764_DMA

実験コード

DMA完了の割り込み処理ルーチンとメインルーチンを以下に示します。コード全体は、mbed.orgにポストしてあります。

static uint32_t DMASrc_Buffer[DMA_SIZE]=
{
    0x01020304,0x05060708,0x090A0B0C,0x0D0E0F10,
    0x11121314,0x15161718,0x191A1B1C,0x1D1E1F20,
    0x21222324,0x25262728,0x292A2B2C,0x2D2E2F30,
    0x31323334,0x35363738,0x393A3B3C,0x3D3E3F40,
    0x01020304,0x05060708,0x090A0B0C,0x0D0E0F10,
    0x11121314,0x15161718,0x191A1B1C,0x1D1E1F20,
    0x21222324,0x25262728,0x292A2B2C,0x2D2E2F30,
    0x31323334,0x35363738,0x393A3B3C,0x3D3E3F40
};

uint32_t DMADest_Buffer[DMA_SIZE];


/*-------------------------MAIN FUNCTION------------------------------*/
extern "C" void DMA_IRQHandler (void);      // extern "C" required for mbed


/*----------------- INTERRUPT SERVICE ROUTINES --------------------------*/
/*********************************************************************//**
 * @brief        GPDMA interrupt handler sub-routine
 * @param[in]    None
 * @return       None
 **********************************************************************/
void DMA_IRQHandler (void)
{
    // check GPDMA interrupt on channel 0
    if (GPDMA_IntGetStatus(GPDMA_STAT_INT, 0)) { //check interrupt status on channel 0
        // Check counter terminal status
        if(GPDMA_IntGetStatus(GPDMA_STAT_INTTC, 0)) {
            // Clear terminate counter Interrupt pending
            GPDMA_ClearIntPending (GPDMA_STATCLR_INTTC, 0);

            if (++TC_count < DMA_CYCLES) {
                // Setup channel with given parameter
                GPDMA_Setup(&GPDMACfg);
                // Run DMA again
                GPDMA_ChannelCmd(0, ENABLE);
                return;
            } else {
                /* DMA run predetermined cycles */
                int elapsedTime =  GetTickCount();
                Buffer_Verify();
                pc.printf("%s", compl_menu);
                pc.printf("DMA %d cycles, %d loop executions\n\r", TC_count, loop);
                pc.printf("Elapsed time %d ms\n\r\n\r", elapsedTime);

                while(1);   // Halt program
            }
        }
        
        if (GPDMA_IntGetStatus(GPDMA_STAT_INTERR, 0)){
            // Clear error counter Interrupt pending
            GPDMA_ClearIntPending (GPDMA_STATCLR_INTERR, 0);
            pc.printf("DMA Error detected.\n\r");
            while(1);       // Halt program
        }
    }
}


/*-------------------------MAIN FUNCTION--------------------------------*/
/*********************************************************************//**
 * @brief        c_entry: Main program body
 * @param[in]    None
 * @return       int
 **********************************************************************/
int c_entry(void)
{
    pc.baud(9600);

    // print welcome screen
    pc.printf("%s", menu);

    /* Disable GPDMA interrupt */
    NVIC_DisableIRQ(DMA_IRQn);
    /* preemption = 1, sub-priority = 1 */
    NVIC_SetPriority(DMA_IRQn, ((0x01<<3)|0x01));

    /* Initialize GPDMA controller */
    GPDMA_Init();

    // Setup GPDMA channel --------------------------------
    // channel 0 (highest priority
    GPDMACfg.ChannelNum = 0;
    // Source memory
    GPDMACfg.SrcMemAddr = (uint32_t)DMASrc_Buffer;
    // Destination memory
    GPDMACfg.DstMemAddr = (uint32_t)DMADest_Buffer;
    // Transfer size
    GPDMACfg.TransferSize = DMA_SIZE;
    // Transfer width
    GPDMACfg.TransferWidth = GPDMA_WIDTH_WORD;
    // Transfer type
    GPDMACfg.TransferType = GPDMA_TRANSFERTYPE_M2M;
    // Source connection - unused
    GPDMACfg.SrcConn = 0;
    // Destination connection - unused
    GPDMACfg.DstConn = 0;
    // Linker List Item - unused
    GPDMACfg.DMALLI = 0;
    // Setup channel with given parameter
    GPDMA_Setup(&GPDMACfg);

    /* Enable GPDMA interrupt */
    NVIC_EnableIRQ(DMA_IRQn);
    
    pc.printf("Start transfer...\n\r");
    GetTickCount_Start();   
    
    // Enable GPDMA channel 0
    GPDMA_ChannelCmd(0, ENABLE);

    /* Wait for GPDMA processing complete */
    while (1) {
        loop++;
    }

    return 1;
}

DMAを何回も起動しながら、平行して117行目のloopが何回まわるかを確認します。CMSISライブラリのexampleからの変更点は以下です:

  • 割り込みハンドラー(17行目)のプロトタイプ宣言にextern "C"をつける。これがないとハングしてしまう
  • コンソールへの文字出力はmbedライブラリのprintf関数に変更
  • 実行時間の計測は1ms周期のSysTick割り込みを使用


実行結果と考察

1行目のDMASrc_Bufferをstatic宣言するとSRAM上に領域が確保され①のSRAM→SRAM DMAになります。1行目をconst宣言するとFlash ROM上に領域が確保され②になります。

①:SRAM→SRAM
DMA 1000 cycles, 22962 loop executions
Elapsed time 5 ms

②Flash ROM→SRAM
DMA 1000 cycles, 7985 loop executions
Elapsed time 5 ms

上記より、期待通り①の方がループの実行回数が多いことが分かります。実行時間は①②とも5msで共通のため、DMA 1回(32 wordの転送)に要する時間は同じで、DMA期間中にDMACとMCUでバスの使用権を融通し合ういわゆるサイクルスチールモードになっていると思われます。

①ではDMACがSRAMアクセス中でもMCUは独立してループ処理の命令フェッチを進めることができ、DMACがバスマスタ権を譲ってくれた隙にloopカウンターの更新値をSRAMに書き戻せるのに対して、②はDMACがROMアクセス中は命令フェッチが止まるためループ回数が低下すると思われます。


おまけ

試しに以下のコードで、DMAと同様のコピーをmemcpyライブラリを使って行いました。

#include "mbed.h"
#include "GetTickCount/GetTickCount.h"

#define LOOP       1000
#define BUF_SIZE   32

Serial pc(USBTX, USBRX); // tx, rx

/* ---- Source data to be copied ---- */
// Allocated to Flash ROM
const  uint8_t __align(8) array1[BUF_SIZE] = {
    0x01020304,0x05060708,0x090A0B0C,0x0D0E0F10,
    0x11121314,0x15161718,0x191A1B1C,0x1D1E1F20,
    0x21222324,0x25262728,0x292A2B2C,0x2D2E2F30,
    0x31323334,0x35363738,0x393A3B3C,0x3D3E3F40,
    0x01020304,0x05060708,0x090A0B0C,0x0D0E0F10,
    0x11121314,0x15161718,0x191A1B1C,0x1D1E1F20,
    0x21222324,0x25262728,0x292A2B2C,0x2D2E2F30,
    0x31323334,0x35363738,0x393A3B3C,0x3D3E3F40
};

// Allocated to SRAM
static uint8_t __align(8) array2[BUF_SIZE] = {
    0x01020304,0x05060708,0x090A0B0C,0x0D0E0F10,
    0x11121314,0x15161718,0x191A1B1C,0x1D1E1F20,
    0x21222324,0x25262728,0x292A2B2C,0x2D2E2F30,
    0x31323334,0x35363738,0x393A3B3C,0x3D3E3F40,
    0x01020304,0x05060708,0x090A0B0C,0x0D0E0F10,
    0x11121314,0x15161718,0x191A1B1C,0x1D1E1F20,
    0x21222324,0x25262728,0x292A2B2C,0x2D2E2F30,
    0x31323334,0x35363738,0x393A3B3C,0x3D3E3F40
};

/*
 * My Memcpy function
 * If source and destination data is aligned at DWROD, performe copy by 8 byts
 */
void Memcpy(void *dst, const void *src, size_t len) 
{
    if ( (uint32_t)dst % sizeof(uint64_t) == 0 &&
         (uint32_t)src % sizeof(uint64_t) == 0 &&
                   len % sizeof(uint64_t) == 0 ) 
    {
        uint32_t *d   = (uint32_t*)dst;
        uint32_t *s   = (uint32_t*)src;
        uint32_t *end = (uint32_t*)(s + len/sizeof(uint64_t));

        while (s != end) {
            *(d++) = *(s++);    // Copy first 4 bytes
            *(d++) = *(s++);    // Copy second 4 bytes
        }

    } else {
        uint8_t *d   = (uint8_t*)dst;
        uint8_t *s   = (uint8_t*)src;
        uint8_t *end = (uint8_t*)(s + len);
        while (s != end) {
            *(d++) = *(s++);    // Copy each byte
        }
    }
}

int main() 
{
    unsigned int startTime, endTime;
    int i,len;
    char __align(8) array3[BUF_SIZE * sizeof(uint32_t)];

    pc.baud(9600);      // set baud rate
    len = BUF_SIZE * sizeof(uint32_t);
    pc.printf("Data Length: %d\n", len);

    GetTickCount_Start();

    /* ---- Flash ROM to SRAM copy ----- */
    pc.printf("Memory copy Flash->SRAM using memcpy lib %d bytes.\n", LOOP*len);
    startTime =  GetTickCount();
    for (i = 0; i < LOOP; i++) {
        memcpy(array3, array1, len);
    }
    endTime = GetTickCount();
    pc.printf("Elapsed time: %d ms.\n\n", endTime - startTime);

    pc.printf("Memory copy Flash->SRAM using My Memcpy %d bytes.\n", LOOP*len);
    startTime = GetTickCount();
    for (i = 0; i < LOOP; i++) {
        Memcpy(array3, array1, len);
    }
    endTime = GetTickCount();
    pc.printf("Elapsed time: %d ms.\n\n", endTime - startTime);


    /* ---- SRAM to SRAM copy ----- */
    pc.printf("Memory copy SRAM->SRAM using memcpy lib %d bytes.\n", LOOP*len);
    startTime =  GetTickCount();
    for (i = 0; i < LOOP; i++) {
        memcpy(array3, array2, len);
    }
    endTime = GetTickCount();
    pc.printf("Elapsed time: %d ms.\n\n", endTime - startTime);

    pc.printf("Memory copy SRAM->SRAM using My Memcpy %d bytes.\n", LOOP*len);
    startTime = GetTickCount();
    for (i = 0; i < LOOP; i++) {
        Memcpy(array3, array2, len);
    }
    endTime = GetTickCount();
    pc.printf("Elapsed time: %d ms.\n\n", endTime - startTime);
}

なんと、SRAM→SRAM、Flash ROM→SRAM共に、1msで終了。loop処理を行っていないという差分はありますが、ひょっとして単なるmemoryコピーならDMAを使うよりMCUでやった方が早いのか?

DMACの設定にSoftware Burst Requestというヤツがあり、こいつを使うとDMACがバスを占有するバーストモードとなり、DMA時間を短縮できるのではと思ったのですが、動かし方が分からず断念でした。


参考情報

Syntax Highlighter 3.0の使用方法

ブログに貼り付けるソースコードを整形(行番号付け、キーワードのハイライトなど)を行うSyntax Highlighterを2.1から最新の3.0に更新しました。私はココログを使用しているため、ココログに依存した情報になりますが設定手順を記載します。

SyntaxHighlighter 3.0での改善点

  • 2.xでは、IEだと横スクロールがうまく表示できず、長い行は折り返し(Wrap Lines)表示にしていたのですが、横スクロールが正常動作するようになりました
  • 行番号を含まないソースコードのコピーはツールバーの"Copy to clipbord"を選択する必要がありましたが、3.0ではブラウザの画面でコピーしたい行を直接選択しCTRL-Cでクリップボードにコピーができるようになりました。また、ソースコードをダブルクリックするとソース全体を選択できます
  • 2.xではソースコードのコピーにFlashを使用していたのですが、最近の風潮(?)に従いFlashレスに・・


SyntaxHighlighterのブログサーバーへのアップロード

  1. SyntaxHighlighterのファイルをここからダウンロードします。
  2. ココログの、「管理ページトップ  >  コントロールパネル  >  ファイルマネージャー」に移動し、以下のフォルダーを作成(フォルダー名はなんでもよいです)
    ・Scripts3
    ・Styles3
  3. ダウンロードしたzipファイルのscriptsフォルダーから整形したい言語に対応したBrushファイルと呼ばれるJavaScriptを、ブログのScripts3フォルダーにアップロード。zipファイルには28種類のBrushファイルが入っていますが、ページ表示毎にサーバーからBrushファイルが読み込まれるため、サーバーレスポンスの遅延を少なくするために必要最小限のBrushファイルを使用するのがよいと思います。私は以下のBrushファイルをアップしています
    ・shCore.js
    ・shBrushCSharp.js
    ・shBrushCpp.js
    ・shBrushJScript.js
    ・shBrushXml.js
    ・shBrushPlain.js
  4. zipファイルstylesフォルダーから、以下のファイルをブログのStyles3フォルダーにアップロード
    ・shCore.css
    ・shThemeDefault.css
    表示テーマを変えたい場合は、shThemeDefault.css以外をアップします。
  5. ブログページにアクセスした際に、SyntaxHighlighterを起動するために、
    「管理ページトップ  >  ブログ一覧  >  PS3とLinux、電子工作も  >  設定  >  ブログの基本情報」
    に移動し、以下の指定を「ブログのサブタイトル(キャッチフレーズ)」の末尾にペースト(http://以下はファイルをアップロードしたブログサーバーのurlを指定)
<link type="text/css" rel="stylesheet" href="http://todotani.cocolog-nifty.com/Styles3/shCore.css" />
<link type="text/css" rel="stylesheet" href="http://todotani.cocolog-nifty.com/Styles3/shThemeDefault.css"/>
<script type="text/javascript" src="http://todotani.cocolog-nifty.com/Scripts3/shCore.js"></script>
<script type="text/javascript" src="http://todotani.cocolog-nifty.com/Scripts3/shBrushCpp.js"></script>
<script type="text/javascript" src="http://todotani.cocolog-nifty.com/Scripts3/shBrushCSharp.js"></script>
<script type="text/javascript" src="http://todotani.cocolog-nifty.com/Scripts3/shBrushXml.js"></script>
<script type="text/javascript" src="http://todotani.cocolog-nifty.com/Scripts3/shBrushPlain.js"></script>
<script type="text/javascript">
SyntaxHighlighter.all();
</script>

Windows Live Writerのプラグイン

私はブログの編集投稿にLive Writerを使っています。先日Windows Live Essentials 2011にアップデートを行ったため2011ベースとなりますが、SyntaxHighlighter用プラグインの設定について記載します。ちなみに、Live 2011のサポートOSにはXPの記載がありませんでした。

SyntaxHighlighter用のプラグインとして、以前はPreCodeを使っていたのですが、Syntax Higlighter 2.0 for Windows Live Writerに変更しました。PreCodeに対して、挿入したコードブロックを<Div>タグでくくってくれるため管理がしやすい点や、挿入後も表示形式などのオプションパラメーターの変更ができる点がナイスです。ただ、デフォルト設定が自分好みでなく変えられない点が惜しい。

Live Writer + SyntaxHighlighterはココログと若干相性が悪く最初にブログエントリを投稿した時点では、SyntaxHighlighterを使ったソースコードをブラウザーで閲覧すると、改行部分に<br></p>などのhtmlタグが混入してしまいます。私の環境では、再度同一エントリを投稿してサーバー上の文書を上書きしてやると余分なhtmlタグが表示されなくなります。あと挿入するオリジナルソースの改行コードも影響している模様で、改行コードとしてCR+LFを使ったWindows形式で保存したファイルからSyntaxHighlighterにコピペしないと、上書きを行っても<br><p>が消えないことがありました。

余計なhtmlタグが挿入された場合、ブラウザを使ってブログ管理画面経由で編集を行うと、余計な改行が大量に挿入されたりして収拾がつかなくなりますので、編集はSyntaxHighlighter一本で行う必要があります。

その他

Windowsクライアントでのブログの見栄え向上のためにメイリオフォントを指定する方法は、以前の設定からカスタムCSSを使用する方法に変更しました。「管理ページトップ  >  ブログ一覧  >  PS3とLinux、電子工作も  >  デザイン  >  カスタムCSSを編集」に移動して、以下のCSS定義を設定します。

body {
    font-family: 'メイリオ', 'Meiryo','MS PGothic', 'Hiragino Kaku Gothic Pro W3', Osaka, sans-serif;
    }
 
textarea {
    font-family: 'メイリオ', 'Meiryo','MS PGothic', 'Hiragino Kaku Gothic Pro W3', Osaka, sans-serif;
    width: 100%;
    }

SyntaxHighlighter 3.0の新機能として、必要なBrushファイルを動的に読み込む「Dynamic Brush Loading」機能があるのですが、「サブタイトル」部分にスクリプト指定を書き込む方法ではうまく動作しませんでした。Dynamic Brush Loadingが動作するときめ細かな書式指定が可能になるので試行錯誤中です。ちなみに、DelphiやErlang用のBrushファイルがあるのに、最近プログラマー人口が急上昇しているであろうObjective-C用のBrushファイルは存在しません。C++用を使えということかな。

« 2010年10月 | トップページ | 2010年12月 »

2018年10月
  1 2 3 4 5 6
7 8 9 10 11 12 13
14 15 16 17 18 19 20
21 22 23 24 25 26 27
28 29 30 31      
無料ブログはココログ