2020年12月31日木曜日

空調服を生体計測的にhack

  ほい。お久しぶりです。

今年はいろいろとありました。会社クビになったり、拾ってくれる会社があったり、拾われた所ではエンジニアのプライドがめっきり折られたりと…散々な感じですね。多分ですけど、1月末には契約が満期になって、また次の会社を探さないといけないですねぇ…。このご時世でどう探せばいいのやら…。


PSoCに逃げ込むぞ!!!


さて。


夏頃に「暑いけど、空調服は空調服のファンが回りっぱなしで身体が冷えすぎて良くないしなんかないかなー」と考えていました。思いつきでは「あ。生体計測していたし、体内調節機能と連動した空調服の制御が出来ないかな?」っと考えを広げていました。空調服で体内調節機能を連動したものは当時(2020年6月)ありませんでした。生体計測とは何かというと非侵襲、つまりは人体を切り開いたりせずに人体の様子を観察できるようにするという医療工学です。昔はこの分野に関わっていたので「どこ」を「どう」測れば「何が分かる」というのは分かりました。ただ今回のような空調服だとかなり難しいのです。なぜかと言うと次のような理由です。

  • 空調服は運動中に着用している。
  • 実験環境のような密室じゃないのでケーブルを吊り下げておけない
  • 野外に出るのであまりの重装備は怪しまれる。
  • 心電計は使えない(ディスポーザブル電極が手に入らない)
だからと言って雑な測定の仕方では生体情報の取得ができないので測定する範囲を絞ることで実現しようと考えました。空調服を制御する方針は次の通りです。
  • 脈拍を測定して運動負荷をみるようにする。
  • 脈拍数と体温は相関するはずなので、体温は測らない。
  • ボルグスケールに応じて空調服を制御する。
  • 外気の温度と湿度を測定して不快係数に応じて空調服を制御する。
このなかで難しいのは脈波測定です。アナログ回路から作る方法もあれば、センサーやモジュールを使うという方法もあります。アナログ回路から作ると大風呂敷になり過ぎるので脈波センサーを探すことにしました。探しているのは耳の血管で測定できるタイプの脈波センサーです。なぜ指ではダメかというときちんとした理由があります。安静時には心拍と末端の脈波はほぼ同じです。しかし運動時は心拍と末端の脈波は全く異なります。これは手先を動かすこともそうですが重力影響や血圧が変化するためです。末端の脈波の中で影響が比較的少ないのは耳や体幹で測ることの出来る脈波になります。
 おおよその方針が出来たので部品探しをしました。ただお目当ての「耳で測るタイプの脈波センサー」がありませんでした。基板から作ればいいのでしょうがそこまで行うエネルギーもなかったので指で測るタイプで妥協することにしました。


さて、使うセンサーの選定も行ったので全体設計を行いました。



メインはPSoC62xAでFreeRTOSが走っている状態です。(FreeRTOSが機能がシンプルで設計も実装も楽です…。)ユーザーボタンは割り込みで検知するようにしてvTaskNotifyGiveFromISR() でFreeRTOS内のタスクへ通知するようにします。そのほかはソフトウェアタイマーで定期的にセンサー値を取得してqueueに詰めて各タスクで使うような作りにしています。LCDならqueueでもらったデータを表示用のメモリに入れる。ファン制御の所はqueueでもらったデータを使ってファンのスピードを変更するというような切り分けです。私の図の書き方はFPGA屋さんのそれと同じなのでタスクはFPGA屋さんで言う所のモジュールみたいな立ち位置になっています。階層化したものを上から見るか横から見るかの差なだけでほとんどやっていることは同じです。

回路やら体表面用のメカ部品やらを作って、実装をするとこんな感じです。





今回は腕に着けられるようにしましたが、これだけでも怪しさが爆発ですね…。
あまりよろしくないです……。
すべてを組み立てた状態はこのような感じです。



プログラムの構造もすごくシンプルなのでかなり堅い作りかなと思います。
プラスアルファでフレームワークとかも入れていないのでメンテナンス性も良いコードになったと思います。

総括
良い感じ。ただ脈波センサーは変えないとかなり残念な感じになりそう。
今回の改造空調服は「バリアスーツ」と呼んで置きましょう。
今後はリュックに制御装置やらバッテリーが入るようにしようと思います。
どんどんプレデターに出てくるようなスーツのようになりそうです。
いっそ、肩からBB弾とかミサイルを発射出来るようにしてしまうのが
面白いかもしれませんね(当面その予定はないですが…)。
ではまたー。

2020年5月28日木曜日

PSoC6向けI2C_CharLCDライブラリを作ってみた。

今回はPSoC6向けにI2CでつながるキャラクタLCD用ライブラリを作ってみました。
PSoC5LPとかでちゃちゃっと表示器が欲しい時にはキャラクタLCDのコンポーネントを使って表示させることが出来ます。PSoC5LPやPSoC4だとキャラクタLCDのコンポーネントはパラレル接続とI2C接続の2つから選ぶことが出来ます。PSoC6でもちゃちゃっと表示器が欲しい所なのですが、リソースがたくさんあるにもかかわらずキャラクタLCDのコンポーネントはありません。パラレルTFTのコンポーネントがあるにはあるのですが毎回持ち出すのはちょっと大げさですし、一部のPSoC6には対応できません。なので今回はPSoC6向けにI2Cで接続するキャラクタLCD用のライブラリを作ることにしました。

 まず、このライブラリについての説明をする前にPSoC6で使われるライブラリを知る必要があります。PSoC6のライブラリはPDLとHAL(以降はCYHALとします)の2つがあります。PDLはペリフェラルドライバーライブラリーの略で文字通りPSoC6のペリフェラルを使うためライブラリーです。CYHALはPDLを元に作られています。おそらく、PDLのバージョンを切り替えても動作することを目的にしているのだと思われます。PSoC6の開発環境はPSoC Creator と modus ToolBoxの2つがあるのですが、PDLはどちらの環境でも使われていますがCYHALライブラリはmodus ToolBoxにだけ使われています。CYHALベースでI2C_CharLCDライブラリを作ってしまうと、PSoC Creatorで使うことが出来ません。なのでI2C_CharLCDライブラリはPDLをベースに作ることにしました。また、PSoC6ではRTOSを多用することも多いのでRTOSを使っても問題が起きにくいようにしました。PDLの厄介な所は構造体やポインタを多用するのでちょっとメモリー周りに意識が向いていないと使いこなせない所ですね。きちんとC言語が分かっている、C言語向けのオブジェクト指向を知っている必要があります。

さて、I2C_CharLCDライブラリの説明に入ろうと思います。AQM0802Aで動作確認をしていますが、コントローラICがST7032であればI2C_CharLCDライブラリが適応できると考えられます。I2C_CharLCDライブラリはmodus ToolBox、PSoC Creatorの両方の環境で動作できます。I2C_CharLCDライブラリの関数は5つと構造体が1つあります。それぞれの関数と構造体は次の通りです。

void I2C_CharLCD_Init(I2C_CHAR_LCD_HANDLE *handle, CySCB_Type *base,cy_stc_scb_i2c_context_t *context)
I2C_CharLCD_InitはI2C_CharLCDで使うI2C_CHAR_LCD_HANDLE構造体の初期化、キャラクタLCDそのものを初期化をします。

void I2C_CharLCD_Clear(I2C_CHAR_LCD_HANDLE *handle)
I2C_CharLCD_Clear()はキャラクタLCDの表示を消します。

bool I2C_CharLCD_Set_Cursor(I2C_CHAR_LCD_HANDLE *handle, uint8_t col, uint8_t row)
I2C_CharLCD_Set_Cursor()は表示のどこから表示をするかを設定します。

bool I2C_CharLCD_PutString(I2C_CHAR_LCD_HANDLE *handle, uint8_t *string, uint16_t length)
I2C_CharLCD_PutString()は文字列を表示します。

void I2C_CharLCD_PutChar(I2C_CHAR_LCD_HANDLE *handle, uint8_t putstr)
I2C_CharLCD_PutChar()は文字を1字だけ表示します。

I2C_CHAR_LCD_HANDLE
I2C_CHAR_LCD_HANDLE構造体はI2C_CharLCDライブラリのすべてで使います。

RTOSを使わない場合はI2CCharLCDConfig.hの#define Free_RTOS_ONをコメントアウトします。RTOSを使う場合はI2CCharLCDConfig.hの#define Free_RTOS_ONと書きます。

サンプルプログラムは次の通りです。
サンプルプログラムmodusToolboxで書きました。

#include "cy_pdl.h"
#include "cyhal.h"
#include "cybsp.h"
#include "FreeRTOS.h"
#include "task.h"
#include "I2CCharLCD.h"
#include "stdio.h"

void ledTask(void* prameter);
TaskHandle_t ledtask_h;

void lcdTask(void* prameter);
TaskHandle_t lcdtask_h;

int uxTopUsedPriority;
cy_stc_i2s_context_t i2cContext;
void I2C_ISR(void);

const cy_stc_sysint_t i2cIntrConfig =
{
    .intrSrc      = I2C_0_IRQ,
    .intrPriority = 6
};

int main(void)
{
    cy_rslt_t result;

    /* Initialize the device and board peripherals */
    result = cybsp_init() ;
    if (result != CY_RSLT_SUCCESS)
    {
        CY_ASSERT(0);
    }
    uxTopUsedPriority = configMAX_PRIORITIES - 1;
    xTaskCreate(ledTask, "LEDTASK", configMINIMAL_STACK_SIZE, NULL, tskIDLE_PRIORITY + 1, &ledtask_h);
    xTaskCreate(lcdTask, "LCDTASK", 256, NULL, tskIDLE_PRIORITY + 2, &lcdtask_h);
    vTaskStartScheduler();

    __enable_irq();

    for (;;)
    {
    }
}

void ledTask(void* prameter)
{

for(;;)
{
Cy_GPIO_Set(LED0_PORT, LED0_PIN);
vTaskDelay(250);
Cy_GPIO_Clr(LED0_PORT, LED0_PIN);
vTaskDelay(250);
}
}


void lcdTask(void* prameter)
{
Cy_SCB_I2C_Init(I2C_0_HW,  &I2C_0_config, &i2cContext);
Cy_SCB_I2C_SetDataRate(I2C_0_HW, 100000, Cy_SysClk_PeriphGetFrequency(CY_SYSCLK_DIV_16_BIT, 0U));
/* Hook interrupt service routine and enable interrupt */
Cy_SysInt_Init(&i2cIntrConfig, &I2C_ISR);
NVIC_EnableIRQ(I2C_0_IRQ);
Cy_SCB_I2C_Enable(I2C_0_HW);

    I2C_CHAR_LCD_HANDLE aqm;

    I2C_CharLCD_Init(&aqm, I2C_0_HW, &i2cContext);
    I2C_CharLCD_PutString(&aqm, (uint8_t*) "count", 5);
    I2C_CharLCD_Set_Cursor(&aqm, 0, 1);

    uint8_t count = 0;
    uint8_t strCount[5] = {};
for(;;)
{
snprintf((char*) strCount, 5, "%d  ", count);
I2C_CharLCD_PutString(&aqm, strCount, 5);
count++;
if(count > 255)
{
count = 0;
}
vTaskDelay(250);
//I2C_CharLCD_Clear(&aqm);
}
}

void I2C_ISR(void)
{
    Cy_SCB_I2C_Interrupt(I2C_0_HW, &i2cContext);
}
/* [] END OF FILE */

サンプルプログラムが正しく動くと次のようになります。



一応ですが、I2C_CharLCDライブラリのソースファイルを置いておきます。

2020年5月8日金曜日

PSoC5LPでDMA転送をする

しばらくぶりです。
世間も世間で大変なことになっておりますが、私も色々と大変なことになってなってきてしまいました。
おし!!PSoCに逃げ込むぞ!!

今回はPSoC5LPでDMA転送をしようと思います。
DMA転送なんじゃいなって言うところに触れる前にペリフェラルへのデータのアクセスについてちょっとおさらい。
 通常のペリフェラルのデータレジスタにアクセスする場合、CPUがペリフェラルのレジスタ番号を使ってデータを取ってきていました。端的に言ってしまえばCPUがレジスタ番号の0x0000番地にあるデータを取得するとかですね。また「ペリフェラル側でデータの用意出来たら、CPUにお知らせしてね。そうしたらCPUがペリフェラルのデータレジスタにアクセスするよ。」っていう割り込み処理(IRQ)を使ったデータレジスタへのアクセスがあります。どちらともペリフェラルからデータを取得する際に「CPUがデータを取得する」という事が発生します。
 「CPUがデータを取得する」というのは時として難しいケースもあります。例えばADCからの連続データを取得したいけど、表示や計算にもCPUパワーを使いたい時。ほかにもありますねSPI通信で大容量のデータをセンサーやモジュールに流し込みたい時等々。こういうケースは結構あります。そこで「CPUを使わないでデータの移し替えが出来ないかしら…。」って登場するのがDMA転送です。

DMA転送についてを触れようと思うと、話が膨らみすぎるのでポイントを絞って進めようと思います。
① DMAコントローラーの行えることについて
② DMAコントローラーが何を手がかりデータを転送しているかについて
③ DMAコントローラーはどのタイミングでデータの転送をするかについて
の3つで考えておくと良いと思います。あえてCPUとペリフェラル間の通信バスについては触れないです。優先順位や通信バスの規格の話になってややこしいので。

① DMAコントローラーの行えることについて
DMAコントローラーの行えることは4つあります。どれもデータを送ったり、受け取ったりすることです。
・メモリー(flashも含め)からペリフェラルにデータを送る。
・ペリフェラルにあるデータをメモリーへ送る。
・メモリーからメモリーへデータを送る(移す)。
・ペリフェラルからペリフェラルへデータを送る。

② DMAコントローラーは何を手がかりにしてデータの転送をしているか
これは3つあるのですがちょっと複雑です。
・ペリフェラルやメモリーに割り当てられているアドレス
・送るデータの総数
・1個当たりのデータ長さ
これらを頼りにDMAコントローラーはデータ転送をします。
これだけだとピンと来ないと思うので簡単に例を挙げようと思います。


ペリフェラル(送り元)からメモリー(送り先)にDMAが転送するとします。
送るデータの総数は32byte、1個当たりのデータは1byteとします。
送り先のメモリーはuint8_tで32byte分の配列になっていてます。(送り先のメモリーは配列じゃないと送れないです。)
DMAコントローラーはペリフェラルのアドレスは分かっているので、ペリフェラルから1個目のデータを取得します。DMAコントローラーは取得したデータをメモリーへ1個目格納します。格納が終わったらまたDMAコントローラーはペリフェラルからデータを取得してメモリーへ格納するわけですがちょっと問題ですね。メモリーのアドレスが分かっている1個目の配列にはもうデータが入っています。そこでDMAコントローラーは転送したデータの個数を数えることで次に格納するメモリーのアドレスを特定することが出来ます。
メモリー側の配列の1番目のアドレスが0x0001であれば、カウンタの値と合わせて2番目は0x0002、3番目のアドレス0x0003というようになります。

③ DMAコントローラーはどのタイミングでデータの取得を行うか
DMAコントローラーにデータの取得を伝えることを「DMA request」といいます。
このDMA requestはデータの送り元がDMAコントローラーに伝えることでDMA転送が開始されます。ペリフェラルでは内部割込みのピンがDMA requestを操作したり、メモリ間での転送はCPUがDMA requestを操作することもあります。
DMAコントローラーへDMA requestを伝えるタイミングはケースバイケースなのです。例を挙げておくと、通信系のペリフェラルで受信時にペリフェラル側のデータが溜まった時にDMA requestを行います。送信時ではペリフェラル側の送るデータが無い時にDMA requestを行います。

ここからは実際にPSoC5LPでDMA転送を行います。
今回はUARTの受信をDMA転送でSRAMに移すことをします。
コンポーネントの接続は次の通りです。


DMA requestは次の通りです。
UARTの場合Level edge設定じゃないとDMA requestがかかりません。


プログラムはFreeRTOSが入った状態で書きました。
現状のコードだとデバッガを当てないと通信テストが出来ないです。

void vGPStask()
{
    uint8_t gpsbuff[32];
   
    /* Defines for DMA_rx */
    #define DMA_rx_BYTES_PER_BURST 1 //1回のデータの長さ(byte単位)
    #define DMA_rx_REQUEST_PER_BURST 1 //request毎に転送
    #define DMA_rx_SRC_BASE (CYDEV_PERIPH_BASE) //転送元
    #define DMA_rx_DST_BASE (CYDEV_SRAM_BASE) //転送先
   
    /* Variable declarations for DMA_rx */
    /* Move these variable declarations to the top of the function */
    uint8 DMA_rx_Chan;
    uint8 DMA_rx_TD[1];
   
    /* DMA Configuration for DMA_rx */
    DMA_rx_Chan = DMA_rx_DmaInitialize(DMA_rx_BYTES_PER_BURST, DMA_rx_REQUEST_PER_BURST,
        HI16(DMA_rx_SRC_BASE), HI16(DMA_rx_DST_BASE));
    DMA_rx_TD[0] = CyDmaTdAllocate();
    CyDmaTdSetConfiguration(DMA_rx_TD[0], 32, DMA_rx_TD[0], CY_DMA_TD_INC_DST_ADR);//DMAのデータのカウントと送る設定
    CyDmaTdSetAddress(DMA_rx_TD[0], LO16((uint32)UART_1_RXDATA_PTR), LO16((uint32)gpsbuff));// 転送元と転送先のアドレス
    CyDmaChSetInitialTd(DMA_rx_Chan, DMA_rx_TD[0]);   
    CyDmaChEnable(DMA_rx_Chan, 1);//DMAの有効化
    UART_1_Start();//UARTの有効化

    for(;;)
    {
        vTaskDelay(250);
    }
}

転送テストを行った結果
tera tarmから文字で「123」と入力しました。
バッファの先頭から1(0x31)、2(0x32)、3(0x33)が入っています。

注意事項
PSoC5LPのDMAは最大で8chまでです。複数のDMAを使う場合はDMA同士の優先順位を決める必要があります。またDMAのカウンタは4095なのでそれを超えるデータの量を取り扱う場合はDMAの設定を組み替える必要があります。DMAと割り込みを組み合わせるケースもあります。DMAのコードジェネレータ(DMA Wizard)の使い方や詳細はアプリケーションマニュアルAN52705 - PSoC® 3 and PSoC 5LP - Getting Started with DMAを読んでください。

2020年1月19日日曜日

PSoC5LPでFreeRTOSを動かす。

今回はPSoC5LPでFreeRTOS V10を動かすことを行った。
以前はCypressのアプリケーションノートの中にFreeRTOSへ適用の仕方が書かれていたと記憶していた。今回調べたところ、アプリケーションノートがそのものがサイト等から消えてしまっていた。FreeRTOSのサイトにはPSoC5LPのサンプルコードがあるため、それをたたき台としてFreeRTOS V10をPSoC5LPへ適用することとした。
 サンプルコードは余計な内容が書かれていた。このままの状態では動作ができないので動作に必要となるコードを取り出して、適用することとした。必須となるスーパーバイザーコールとシスティックの部分はmain.c内に書かれていたので、新しい適用もこれに沿えば大丈夫だと思われる。使い方はスレッドスタートやタスクの登録以前に実行する。
下記はスーパーバイザーコールとシスティックのプログラム部分となる。
extern void xPortPendSVHandler( void );
extern void xPortSysTickHandler( void );
extern void vPortSVCHandler( void );
extern cyisraddress CyRamVectors[];

/* Install the OS Interrupt Handlers. */
CyRamVectors[ 11 ] = ( cyisraddress ) vPortSVCHandler;
CyRamVectors[ 14 ] = ( cyisraddress ) xPortPendSVHandler;
CyRamVectors[ 15 ] = ( cyisraddress ) xPortSysTickHandler;

あとはFreeRTOSでダウンロードしたソースファイルからCortexM3用のFreeRTOSデータをまとめれば、FreeRTOSに必要なデータがそろう。注意が必要な点はportableディレクトリ内のデータとなる。このディレクトリはCPUのコアとコンパイラ毎に異なったシスティックのプログラムとスーパーバイザーコールのプログラムが保存されている。さらにFreeRTOSが使うメモリー管理アルゴリズムのプログラムもここに格納されている。システィックのプログラムとスーパーバイザーコールのプログラムが書かれたファイルは、すべて同じファイル名となっている。つまり、ファイルの操作を間違えるとプログラムが変質することになる。
下記は今回参照したファイルのディレクトリ構造
FrerRTOSv10.2.1
┣FreeRTOS
┃┣Demo(MCUごとに作られたデモプログラムの一式)
┃┣License
┃┗Source(FreeRTOSの本体)
┃ ┣include
┃ ┗portable
┃  ┣MemMang(heap_1 heap_2 heap_3 heap_4 heap_5のいずれかが必要)
┃  ┣GCC
┃  ┃┣ARM_CM3(port.c portmacro.hがある。)
┃  ┃┗そのほかのCPU用ディレクトリ
┃  ┗そのほかのコンパイラ用ディレクトリ
┗FreeRTOS+Plus

下記はFreeRTOSのソースファイルから構成したファイル
freertos
┣inc
┃┗


┣src
┃┗

freeRTOSConfig.h

構成したソースファイルはエクステンディングアイテムを用いてPSoCCreatorに導入した。ビルドはFreeRTOSを入れたのでそれらを参照できるように設定を行った。

動作確認はLEDの点滅を行った。下記はそのプログラムとなる。
#include "project.h"

#include "FreeRTOS.h"
#include "task.h"
#include "queue.h"

void vLEDtask();
xTaskHandle xLEDtask;
uint8_t ucParameterLED;

int main(void)
{
    CyGlobalIntEnable; /* Enable global interrupts. */
    /* Port layer functions that need to be copied into the vector table. */

    extern void xPortPendSVHandler( void );
    extern void xPortSysTickHandler( void );
    extern void vPortSVCHandler( void );
    extern cyisraddress CyRamVectors[];

/* Install the OS Interrupt Handlers. */
CyRamVectors[ 11 ] = ( cyisraddress ) vPortSVCHandler;
CyRamVectors[ 14 ] = ( cyisraddress ) xPortPendSVHandler;
CyRamVectors[ 15 ] = ( cyisraddress ) xPortSysTickHandler;
 
    xTaskCreate(vLEDtask,"ledtask",configMINIMAL_STACK_SIZE,&ucParameterLED,tskIDLE_PRIORITY + 1,&xLEDtask);
 
    /* Place your initialization/startup code here (e.g. MyInst_Start()) */
 
    vTaskStartScheduler();
    for(;;)
    {
        /* Place your application code here. */
    }
}

void vLEDtask()
{

    for(;;)
    {
        Pin_1_Write(1);
        vTaskDelay(250);
        Pin_1_Write(0);
        vTaskDelay(250);
    }
}

void vApplicationStackOverflowHook( TaskHandle_t pxTask, char *pcTaskName )
{
/* The stack space has been execeeded for a task, considering allocating more. */
taskDISABLE_INTERRUPTS();
for( ;; );
}
/*---------------------------------------------------------------------------*/

void vApplicationMallocFailedHook( void )
{
/* The heap space has been execeeded. */
taskDISABLE_INTERRUPTS();
for( ;; );
}
/*---------------------------------------------------------------------------*/



PSoC5LPはPSoC6で使われているPDLが使われていない。PLDがあるとFreeRTOSの適用が楽なのだが、それらしいオプションも見当たらなかった。PSoC5LPでFreeRTOSの開発を進める場合はこれを参照に進めればよいと思われる。

2019年10月27日日曜日

PSoC6 でLEDちかちか。

テキトーに放置して2年弱になりますが
プライベートの方が相当もめたり、ぐじゃぐじゃになったりしていました。
まー、就職できねーとかあったんです。
面接官から
「よく分らんイベント(PSoCまつり)やっているから…。」
「プライベートで開発しているとかってどうかしているとか…。」

モノ作ったりが好きで、プログラム書いたりするのも嫌いじゃないだけなんですけどね。
「今回はご縁がなかったことで。」
ばっかし…。
じゃ、そもそもエンジニアの求人票出すなやコラー!っていう…。

愚痴はほどほどでPSoC6でLEDチカチカさせようと思いますー。

PSoC6はPSoC5LPの後継機になるのですがPSoC5LPの面影はほとんど無いくらい
のPSoCでほとんど

「CPUパワーで殴り倒す」

って言う代物です。PSoCなのか??って思うほどです。

んで、いままでのPSoCCreatorの開発環境ではなく
Modus Toolboxというeclipseベースの開発環境に切り替わってしまいました。
インストールそのものはブログで取り上げるほどでもない位簡単なので割愛します。

さて、ここから先が結構厄介で、modus Toolboxの環境下というかPSoC6用ライブラリーも含めなのですが「FreeRTOS使ってね。それ前提だから」みたいな感じになっています。

このブログでFreeRTOSの仕様の解説までは出来ないので
簡単に触りを触れると、
「小さいプログラム(タスク)をつくって、全体のプログラムを分けて動作させよう。」
っというフレームワークです。

プロジェクトの新規作成は次の3ステップです。
①ファイルからmodus toolbox IDE application を起動させます。


②使用するPSoC6のボードを選びます。


③emptyのプロジェクトを選んで、application nemeを変更すれば出来上がり。



この状態だとRTOSもなにも入っていない状態のプロジェクトが出来上がります。
まずはRTOSの導入と、GPIOの設定をしてしまいましょう。
RTOSの導入をします。
まずは「Library Manager」をクリックして次の画面を出します。
Libraryのタブをクリックし、RTOSのチェックボックスを入れます。

そのあと、applyをクリックして画面を閉じます。

GPIOの設定はDevice Configuratorで行います。
GPIOの設定は次の通りです。

こちらはOKボタンがないのでFile→seveをクリックします。
ここまででとりあえず一区切りです。

libディレクトリのfreertosディレクトリにFreeRTOSConfig.hというRTOSに対しての設定を行うヘッダファイルがあるのでこれをルートディレクトリへ持っていきます。


FreeRTOSConfig.h
でヒープのアルゴリズムを次の通りにしました。

#define configHEAP_ALLOCATION_SCHEME (HEAP_ALLOCATION_TYPE4)

main.cのプログラムは次の通りです。

#include "cy_pdl.h"
#include "cyhal.h"
#include "cybsp.h"
#include "freeRTOS.h"
#include "task.h"
void led_blink(void);
TaskHandle_t led_blink_Handle;

int main(void)
{
    cy_rslt_t result;

    /* Initialize the device and board peripherals */
    result = cybsp_init() ;
    if (result != CY_RSLT_SUCCESS)
    {
        CY_ASSERT(0);
    }
    __enable_irq();
    xTaskCreate(led_blink, "LED_BLINK", 128, NULL, 3, led_blink_Handle);

    vTaskStartScheduler();

    for(;;)
    {
    }
}

void led_blink(void)
{
init_cycfg_pins();
cyhal_gpio_write(LED3_HAL_PORT_PIN, 1);
for(;;)
{
vTaskDelay(250);
Cy_GPIO_Write(LED3_PORT, LED3_NUM, 0);
vTaskDelay(250);
Cy_GPIO_Write(LED3_PORT, LED3_NUM, 1);
}
}
/* [] END OF FILE */


cyhal_gpio_wite()はHALでのGPIO操作関数です。
Cy_GPIO_Write()はPDLでのGPIO操作関数です。

HALとPDLの2つがありますが、
明確にどちらを使うのが適切なのかよくわかりません。
サンプルコードを読むとHALを使ってほしいように見えます。
ですが、ドキュメント読みやすさからするとPDLかと思いました。
またHALもPDLを操作するような書かれ方をしていました。

PS.
なんでレベル4かというと、RTOS使うから。
RTOSのフレームワークって結構奥が深いので。

2016年12月21日水曜日

感雨センサーをつくってみる。

今回はオペアンプとADCを組み合わせて、感雨センサーを作ってみました。
今回のは今までの記事を総合していけば難しい話ではない。

感雨センサーは割とお安く売っているのだけれど、
MEMS、つまりは専用ICになっているというわけじゃ無くて
むき出しの配線に水滴が当たると抵抗値が下がると言う仕組み。
ひずみゲージとかと同じで、
どう考えてもアナログフロントエンドが必要な類のセンサーです。

さて、分圧回路をするか差動増幅みたいな感じなるのかなと考えてチョロット
実験をしてみました。結果から言うと分圧してあげる方がよさそうという事がわかりました。しかし、感雨センサーに水滴を付けたりさせても200KΩ~1MΩぐらいの変化なので分圧回路に使う抵抗は変に小さくすると反応が出ないので「どうしたもんかね?」と言うのが出る訳ですな。

通常のMCUだと「どうしたもんかね…」でおしまいになるのですが、
PSoCなら分圧回路から出力される電圧値をオペアンプで増幅してあげれば
ADCにつなげた時にADCの分解能を最大限使える様になるはずです。
これは今回構成したPSoCの中身です。



分圧回路→オペアンプで1.5倍に増幅→ADCでデジタルに変換
デジタル値はI2CのLCDへ表示する。と言う感じ。
ただし、デジタル値は乾いているとき(抵抗値が大きいとき)は
電圧が大きくなる。なのでそこは気を付けないといけない。



MCUで同じことをするにはオペアンプを買ってきて、
電流制限を付けた電源を作ったりして流れる電流量を調整したり
(しないと配線むき出しのセンサーが熱を持つ可能性がある)
と非常に面倒臭い…。
ま、PSoCだとセンサーの性質を調べる方に時間がかかったけど
割と簡単にできちゃいます。

温度や湿度、気圧などは専用のICとして売られているけれど
今回みたいに物理的な現象を電圧値に変えてセンサーとして使う場合は
アナログフロントエンドを避けて通る事は中々難しい。
また、王道のアナログ回路をするのは部品や機材も多く必要になる。
けどPSoCであれば、途中から部品を買い足す事も少なくなるので
お財布にも優しく、多種多様構成が出来るのでうまく使って
高速に作りたいシステムや作品を作りましょう!!

2016年12月16日金曜日

ルックアップテーブル(LUT)ってなんじゃろ

アドベントカレンダーの15日目

さて、前回はオペアンプとコンパレータ―をつかってアナログ回路の序盤を試してみました。
今回はその対極側のデジタル回路の方にトライして見ようと思います。

「デジタル回路って言うとソフトウェアのことでしょ!」と思う人も多いかも知れない。
けれど、今回はCPUによるソフトウェアのパワーと言うわけでは無く
ある意味正統なデジタル回路、つまりは
0と1(OFFとON)の組み合わせによって動く回路
の論理回路をしてみる。

 論理回路は簡単に言うとある入力の組み合わせの時に、ある出力がされる」のこと
だと考えてもらえば大筋間違いはないだろう。
論理回路では入出力の組み合わせのことを真理値表と言う。
試しにAND回路とOR回路の入力と出力の真理値表を見てみよう。




これはCypressのANDとORのデータシートの抜粋です。
真理値表では1と0で表現されていて
「0」OFFもしくはLowのことで、日本語だと偽
「1」ONもしくはHighのことで、日本語だと真
となっている。

AND回路は入力が片方だけではなく、両方ともの入力が入ると出力される。
OR回路ではどちらかの入力が入っていると出力がされている。
王道では「または×の時」の論理和や論理積、否定論理を知って置くべきだが、
カルノー図をつかって縮小した論理作り出すことも珍しいので
細かい部分は電子回路の本を見てほしい。

今回はカルノー図や、論理回路の組み立て方になじみがなくても、論理回路を作る方法がある。
それはルックアップテーブル(LUT)を使う方法になる。

今回はタクトスイッチ3つとLED4つをつかって、ルックアップテーブルを試してみようと思う。
タクトスイッチは始めの状態が「OFF」、「Low」の状態つまりは、正論理でないといけないので
スイッチの配線には注意が要る。
それでは試してみよう。


空のプロジェクトを作ったら、
今回は出力ピンが4つ、入力ピンが3つ使う。なので、DigtalOutが4つ、DigitalInが3つをドラックアンドドロップする。


DigitalInの初期状態がLowの状態でないといけないため、内部に接続する抵抗の設定をPullDownにする。よくある電気工作の本だとPullUpと書いてあることが多いと思うが、今回はこれで大丈夫なので進めてほしい。


ここまでできたら、Digital→logicの中になるLookup table
ドラックアンドドロップしてトップデザインに入れる。



こんな感じになる。



Lookup tableのコンポーネントをダブルクリックして、設定を開くと
こんな感じになっている。



ルックアップテーブルと言うのは真理値表をそのまま入力すれば、動作する
コンポーネントになっている。しかし、入力数と出力数には制限があるので注意してほしい。
表の左側の灰色側が入力側黒の縦線を挟んだ右側が出力側となっている。
今回は3入力、4出力となっているので、増やす必要がある。
左上側のフォームにinput outputが書いてあるここを操作して3入力4出力にしてほしい。
操作し終わるとこんな感じ。



ここまで出来たら、入力の組み合わせに対する出力の設定をしていこうと思う。
試しに、出力側の表の「0」をクリックしてみると「1」に変わるだろう。
逆に「1」をクリックすると「0」に変わるだろう。
つまり左側の組み合わせの時に、任意の出力の組み合わせとなることになる。
今回はこのような組み合わせにしてみた。


任意の設定が出来たら「OK」をクリックして閉じよう。
ここまで出来たら、LUTと入出力のピンを接続しよう。 
接続し終わるとこのような形になる。


ここまで出来たら、使うPinの番号もしくはポート番号を左側のメニューバーにある
「Pin」をダブルクリックして設定をしよう。
今回は特に制約がないので任意のポートに設定をします。
ここまで出来たら、コンパイルをしよう。
コンパイルをしている間に、実際の配線をしましょう。
こんかいはこんな感じで配線をします。
スイッチの配線は間違えないようにしないと正しく動作しないので注意が要ります。


配線が終わったら、プログラムは一切書かないのでこのまま書き込みます。
書き込みができたら、タクトスイッチを押して
ルックアップテーブルの設定通りに動くか試してみよう。
正しく動いたら成功だ!
(/・ω・)/

さて、今回はタクトスイッチとLED4つをつかってルックアップテーブルを試してみました。
ルックアップテーブルは内部の設定次第で今回みたいな使い方(デコーダー)や
カウンタ―にも変わる便利な道具です。
使い方次第で、ロボットやLEDの動作にも使えるので
うまく使うと時間的な制約が緩くなるのでプログラミングも楽になると思います。

上手く組み合わせるとこういう風な動作もプログラミングをしなくても出来るようになります。