home.gif
Visitor No. 39265

短縮URL https://bit.ly/2QWLHid

Cypress Semiconductor社PSoC Creator 関連のメモです。ここでは、PSoC4を使用しますが、PSoC3, PSoC5でも同じです。


目次

メモ

PSoC4ファミリー(ARM Cortex-M0版PSoC)の選択

ADCがSAR 1個だけであることを除くと低消費電力で使いやすい。BluetoothLE搭載版もある。

CY8C4000系クロック16MHz, フラッシュ16kB, シリアルポートはI2Cのみ(送信用ソフトウエアUARTはあり)
CY8C4100系クロック24MHz, フラッシュ32kB, I2C/SPI/UART2個, LCD, 12bit-ADC
CY8C4200系クロック48MHz, フラッシュ32kB, DMA, I2C/SPI/UART4個, CAN2個, LCD, 12bit-ADC, プログラマブルロジック

電源構成

VDDD, VDDA, VDDR, VCCDなどいろいろあって忘れそうなのでデータシートを要約。

power_supply.png
  • 電源電圧は、1.8-5.5V
    • Bluetoth LE搭載ファミリは、1.9-5.5V(1.9V以下でも動作するが、BLEは停止)
  • 内部LDOはデフォルトで有効
    • 無効にした場合は、VDD1.71-1.89V、VCCDとVDDDをショートする

低消費電力モード

PSoCでは、チップ全体の電力モードの他に、各回路モジュール毎の電源ON/OFFをプログラムで個別に切り替えることができる。全体の電源モードは、5段階用意されているが、定期的に動作させてデータを取得する場合は、スリープまたはディープスリープが使用できる。外部からの何らかのイベントで動作させる場合には、ハイバネートかストップが有用。ここでは、PSoC 4 の電力モードについて、データシートをまとめておく。

モード消費電力アクティブへの復帰方法
アクティブ1.3mA - 14mA-
スリープ1.0mA - 3mA任意の割り込み
ディープスリープ1.3uA - 15uAGPIO, WDTタイマー, I2C, コンパレータ, 外部リセットピン
ハイバネート150nA - 1uAGPIO, コンパレータ, 外部リセットピン
ストップ20nA - 80nAWakeupピン, 外部リセットピン

MiniProg3の接続

純正の書き込み器MiniProg3によるPSoC生チップのプログラムに関する覚え書き。もっとも、MiniProg3は値段が高いので、マニアには敬遠されているらしい。PSoC Programmerを起動して、Utilitiesタブからファームウエアをアップデートしておく(古いファームウエアだと、Power Cycleモードが使用できないと聞いている)。

HSSP.png
  • プログラムとデバッグにSWD(ARM Serial Wire Debug)を利用する
  • どのポートがSWDかは、データシートでSWDを検索する
  • SWDポートは汎用IOと共有なので、プログラム専用にするとSWD_IO, SWD_CLKを割り当てた2ポート分の汎用IOが使えなくなることに注意

GPIO(PSoC Creatorの基本操作)

学部生、卒研生向け、プログラムとデバックの手順解説。ここでは、CY8CKIT-142 PSoC4 BLEモジュール(CY8C4247LQI-BL483搭載、技適認証済み)を使用しましたが、BLEを使わないなら PSoC4 CY8C4245 SSOP版あたりも使いやすい。

  • PSoC外部の回路製作
    • 下図のようなタクトスイッチを押すとLEDが点灯する回路例を考える。電源VDDDは、MiniProg3から供給(VTARG)する。
led_sw.png
  • PSoC内部の回路設計
    • PSoC Creatorを起動
    • File - New - Project...
      • Taget device = PSoC4, PSoC4200BLE (使用するデバイスに合わせて) - Nextボタン
    • Empty schematic - Nextボタン
      • Workspace name, Location, Peoject nameを適当に設定 - Finishボタン
    • 右欄のComponent Catalog - Cypressタブ - Ports and Pins - Digital Input Pin と Digital Output Pin をドラッグして各1個づつ回路図シート(TopDesign?.cysch)に配置
      • Componentを選んでComponent Catalog下のOpen datasheetの文字をクリックまたは配置した部品を右クリックして、ポップアップメニューから Open Datasheet... を選ぶと、選んだComponentのデータシートが表示され、Find Code Example...を選ぶとプログラム例のリストが表示される
    • ここでは内部回路を作成しないので配線を行う必要はないが、回路の配線が必要な場合は、回路図欄左端のツールボックスの配線アイコンをクリックしてから、回路図上で配線を行う
pin_01s.png
  • 回路図に配置したPinのシンボルをダブルクリックして、下記のように設定 - OKボタン
    • HW connection: プログラム制御の場合チェック無し、回路制御の場合チェック有り
    • Drive mode: 出力回路の設定。スイッチ入力の場合はResistive pull up (押さないときHighとする)、LED点灯の場合はStrong
    • Initial drive state(初期状態のHigh, Lowに合わせる)
    • Analog 入力と出力の場合は、原則としてHigh impedance analog を選択する

入力ピン pin_input.png

出力ピン pin_output.png

  • 左欄のWorkspace Explorer - Sourceタブ - Design Wide Resource - Pins をダブルクリック
  • 右欄のPin_1, Pin_2 をピンアサイン図(Design01.cydwr)のポートP2[2], P2[3]にそれぞれドラッグするか、右欄のPort列のドロップダウンリストから、割り当て先のポートP2[2], P2[3]を選択する
pin_02s.png
  • プログラムの作成
    • 左欄のWorkspace Explorer - Sourceタブ - Source Files - main.c をダブルクリック
    • 下図のようにmain.cのプログラムを入力
      • 通常、無限ループ for(;;) の上には、電源投入時の初期化処理のコードを入力し、for(;;)ループの中に通常のプログラムを入力する
      • CyGlobalIntEnable?; では、CPUへの割り込みを許可している(このプログラムでは割り込みを使用しないので、無くてもよい)
      • Pin_2_Write(!Pin_1_Read()); では、Pin_1の状態(H or L)を読み取り、Pin_1の否定をPin_2に出力している
    • ツールバーの Build ボタンをクリックして、プログラムをビルドする
pin_03s.png
  • プログラムの書き込み
    • MiniProg3を回路基板のピンヘッダに挿す
    • メニューより、Debug - Select target and program...
    • MiniProg3を選択
      • Port Settingボタンをクリックし下図のように設定して、OKボタンをクリック
    • PSoC 4200 BLE CY8C4247LQ*-BL483(ターゲットのデバイス)を選択して、OK/Connectボタンをクリック
      • しばらく待つと書き込みが始まる
program_port.png
  • MiniProg3から電源供給されているので、MiniProg3を挿したままで動作確認を行う
  • デバッグ

PSoC Creatorのデバッグ機能は、非常に便利で、回路基板上でレジスタ、変数の変化を簡単に把握できる。

  • ツールバーの Debugボタンをクリックすると、プログラムの書き込みが行われ、続いてデバッグモードになる
debug_01zs.png
  • プログラムコードが表示されているウインドウ左側のグレーの列をクリック
    • プログラムの行に赤丸が付いて、ブレークポイント(プログラムを一旦停止させる場所)が設定される
  • Step Overボタンをクリックして、関数毎にプログラムを実行して動作を確認する
    • Step Into で、関数内部に入り、Step Out で関数の実行前に戻る
    • タクトスイッチを押しながら Step OverボタンをクリックするとLEDが点灯する
debug_03zs.png
  • Stop Debuggingボタンで、デバッグモードを一旦終了し、上図のようにプログラムを追加(例として変数の内容を確認してみる)
  • 再度、Debugボタンをクリックし、動作を確認する
  • 下の方の Localsタブをクリックすると、変数 x の値が表示される
    • Step Overボタンをクリックすると、xの値がインクリメントされることが確認できる
    • Registersタブをクリックすると、各種レジスタの内容が表示される
    • 注意:変数に値を代入しても、値がプログラム中で使用されない場合は、Localsに変数が表示されない

I2C LCDと電源制御

小型キャラクタ液晶ディスプレイの使用例。3.3V動作、I2C接続の Xiamen Zettler Electronics社 AQM0802A-RN-GBW を使用。秋月でピッチ変換キット(AE-AQM0802)が販売されている。また、電源制御例を示す。LCDは低消費電力だが、1mA程度消費するので、小容量バッテリーや環境エネルギー駆動の場合、スイッチを押したときだけ表示し、しばらく表示したら電源を切る。電源は、PSoCのGNDとは分離されたCGNDラインに接続し、CGNDをPSoCのIO(オープンドレイン)でON/OFFする。VDDはON/OFFせず、パスコンに充電した状態を保つ。

lcd.jpg
lcd_pwr.png
  • I2C (SCB mode), Character LCD with I2C interface, Digital Bidirectional Pin 2個, Digital Input Pin, Digital Output Pin を配置する。I2Cの設定を Show I2C terminals に設定してからI2CモジュールとDigital Bidirectional Pin の配線を行う。
lcd_01.png
  • I2Cの設定
    • PSoCをマスター、LCDをスレーブとする。
lcd_02.png
lcd_03.png
  • I2C_LCDの設定
    • LCDのスレーブアドレスは、0x7C(データシートで要確認)
    • 基本コマンドはデフォルトで設定されているので、カスタムコマンドを追加する。
      • ContrastSet?のCMD byte 2 = 0x70 の1桁目(0)を変更すると、コントラストが変えられる。
lcd_04.png
lcd_05.png
  • Digital Bidirectional Pinの設定(I2C_SCL, I2C_SDA)
    • 外付けプルアップ抵抗を使用しない場合は、PSoCのピン設定を Resistive pull up にする。外付けプルアップ抵抗(LCDモジュール変換基板に搭載)を使用する場合は、Open drain, drives low にする。
lcd_06.png
lcd_07.png
  • Digital Output Pin, Digital Input Pinの設定(CGND_1, SW_IN_1)
    • CGND_1は、Open drain, drives low(出力がLowでない時は、オープンとなり電流が流れない)。
    • SW-IN_1は、Resistive pull up(スイッチを押さないときはHigh、スイッチを押すとLow)。
lcd_08.png
lcd_09.png
  • main.cの記述
    • SW_IN_1_Read()でスイッチを読み取り、LowだったらCGND_1をLowにしてLCDの電源を入れる。
    • LCDの初期化、カーソル位置設定、文字の出力を行い、一定時間待ってから、CGND_1をHighにしてLCDの電源を切る。
#include "project.h"

void I2C_LCD_Init(void) {
  CyDelay(40u);     //40ms waiting
  I2C_LCD_1_FunctionSet_Nor();
  I2C_LCD_1_ReturnHome();
  I2C_LCD_1_FunctionSet_Ext();
  I2C_LCD_1_InternalOscFrq();
  I2C_LCD_1_ContrastSet();
  I2C_LCD_1_PwrIconContrast();
  I2C_LCD_1_FollowerCtrl();
  CyDelay(200u);     //200ms waiting
  I2C_LCD_1_FunctionSet_Nor();
  I2C_LCD_1_DisplayOn();
  I2C_LCD_1_Clear();
  I2C_LCD_1_EntryModeSet();
  //I2C_LCD_1_WriteControl(0x0fu); //Disp:On Cursor:On Position:On
}

void I2C_LCD_Location(uint8 row, uint8 column) {

  if(row == 0){
    I2C_LCD_1_SetDDRAM(0x80u + column);
  }else{
    I2C_LCD_1_SetDDRAM(0xC0u + column);
  }
}

int main(void)
{
    CyGlobalIntEnable; /* Enable global interrupts. */

    // LCD CGND = Low
    CGND_1_Write(0);
    
    // Initiallization of I2C and LCD
    I2C_1_Start();
    I2C_LCD_1_Start();
    
    for(;;)
    {
        // LCD CGND = Low
        if(!SW_IN_1_Read()) {
            CGND_1_Write(0);
            I2C_LCD_Init();
            CyDelay(100u);
        
            // LCD output
            I2C_LCD_Location(0u,0u);
            I2C_LCD_1_PrintString("Enjoy,");
            CyDelay(1000u);
            I2C_LCD_Location(1u,0u);
            I2C_LCD_1_PrintString("PSoC4!");
            CyDelay(1000u);
            I2C_LCD_1_Clear();
            
            // Software delay loop (ms)
            CyDelay(1000u);
            
            // LCD CGND = High
            CGND_1_Write(1);
        }
    }
}

UART

PCや無線モジュールとの標準的な接続方法の例。TXとRXは互いにクロスさせることに注意。プルアップ抵抗は、PSoCの入出力ピンで設定することもできるが、外部抵抗の使用が推奨されている。複数トークンを含む場合の受信処理は、ADCとIDACの項を参照。写真は、PCとの接続テストに使用した USB-Serial変換モジュール(スイッチサイエンス FTDI USBシリアル変換アダプター Rev.2)。

uart.jpg
uart.png
  • UART(SCB mode)を配置し、rx_inにDigital Input Pin, tx_outにDigital Output Pin を配線接続する
    • UARTには下記の3種類の実装方法がある。UART (SCB mode)は、ハードウエアリソースを消費しないが、ピン配置に制約がある。
      • Software Transmit UART: CPUを使って実行する。送信専用。
      • UART (SCB mode): SCB(シリアル通信用ハードウエア)で実装する。
      • UART:UDB(汎用ディジタルブロック)を使って実装する。
uart_01.png
  • UART(SCB mode)の設定
    • ここでは、Baud rate = 115200bps, Oversampling = 8 に設定。クロック周波数との関係により、実際の通信速度には、誤差が発生するので、誤差が 2.5% より小さくなるように、適当なOversamplingを設定する。実際の通信速度は、Applyボタンをクリックすると、Baud rateの右側に表示される。
uart_02.png
uart_03.png
uart_04.png
  • 入出力ピンの設定
    • 外付けのプルアップ抵抗を想定し、ここではプルアップしない。
uart_05.png
uart_06.png
  • main.cの記述
    • UARTポートから文字列を受信し、Received Data = に続けてエコーさせる例。
    • ここでは、受信デリミタとして、CR (0x0d)を使用し、送信には、CR+LF (0x0d, 0x0a)を使用している。
    • UartGetChar?()は、バッファにデータが無いときに、NULL (0x00)を返すので、CRを検出するまで、NULLは読み飛ばしている。
      • ヒント:1回のRXバッファ読み出しまでに、全データを受信しているとは限らないため、CRを検出するまでNULLデータは読み飛ばす必要がある。また、読み出し用文字配列は、前回のデータを消すためNULLで初期化する必要がある。
    • FTDT USB-Serial変換器を接続した状態では、PSoCの書き込み時にエラーが発生することがあるので、USB-Serial変換器を外してから書き込む。
#include "project.h"

int main(void)
{
    CyGlobalIntEnable; /* Enable global interrupts. */

    // Initiallization of UART module
    UART_1_Start();
    UART_1_UartPutString("UART with PSoC \r\n");
    uint32 rxData;
    uint8  ptr;
    char   strData[] = "";
    
    for(;;)
    {
        // Checking ASCII char in RX buffer
        // If you need to check non-ASCII code, use UartGetByte()
        rxData = UART_1_UartGetChar();
        if(rxData) {
            memset(strData, '\0', strlen(strData));
            ptr = 0;
            
            // Read until CR delimiter
            while(rxData != 0x0d) {
                if(rxData != 0x00) {
                    strData[ptr] = rxData;
                    ptr++;
                }
                rxData = UART_1_UartGetChar();
            }

            // NULL termination
            strData[strlen(strData)] = '\0';

            // Clear RX buffer and RX FIFO
            UART_1_SpiUartClearRxBuffer();

            // Echo of RX data
            UART_1_UartPutString("Received Data = ");
            UART_1_UartPutString(strData);
            UART_1_UartPutString("\r\n");
        }
    }
}

ADCとIDAC

IDAC(Current Digital to Analog Converter)から出力した電圧をADC(SAR Analog to Digital Converter)で測定する例。IDACは、電流出力のため、出力端子に接続した抵抗によって、出力電圧レンジが決定される。

  • アナログ回路を使用するため、VDDAにアナログ電源(3.3V)を供給する。この事例では、ADCのリファレンス電圧にVDDAを使用しているため、VDDAの精度に注意が必要。
  • ADCの入力にバッファ(ボルテージフォロワ)を接続した場合と、しない場合を比較するために、IDACの出力をジャンパーを介して2ポートのADCに接続する。
  • IDACの出力電圧の設定とADC結果の読み出しのためにUARTを使用する。PC等から、DAC 3.3 のようにコマンドを送信して、IDACの出力電圧を設定する。PSoCは、約1秒ごとに、ADCでサンプリングを行い、UARTに送信する。ただし、この例では、サンプリングをソフトウエアトリガで行っているため、サンプリング周期は正確ではない。
    • DAC 以外のコマンドを送ると、Unknownと表示され、IDACの出力は更新されないようにする。
  • IDACは、8bit 電流DACであり、LSB = 1.2mA または 2.4mAから選択する。
    • LSB = 1.2uA とすると、I = 1.2uA * Digital, V = R * I = 1.2u * R * Digital
    • R = 10kohm とすると、V = 0.012 * Digital (V), 最大値 = 0.012 * 256 = 3.06V
adc.png
  • Component Catalog から以下の部品を選んで配置する。
    • Analog - ADC - Sequencing SAR ADC 1個
    • Analog - Amplifiers - Opamp 1個
    • Analog - DAC -Current DAC 1個
    • Communications - UART (SCB mode) 1個
    • Ports and Pins - Analog Pin 3個
    • Ports and Pins - Digital Input Pin 1個
    • Ports and Pins - Digital Output Pin 1個
adc_01.png
  • 各コンポーネントを下記のように設定。
adc_02.png
  • Sample mode を Hardware trigger に設定
    • 一定周期で連続サンプリングを行う場合は Free running, 1サンプリングする場合は Hardware trigger を選択。プログラムでトリガをかける場合も、Hardware trigger を選択。
  • ここでは、Input range = VDDA(3.3Vを想定)、Single ended negative input = VSS (0V)
    • この設定では、VSS(0V) から VDDA(3.3V)が入力電圧範囲となる。
    • Single ended result format = Signed しか選べない (選択欄がグレーになってUnsignedは選択できない)。従って、Single ended negative input を 0V に設定すると、 0 - 3.3V が、12bitの正値(0 - 2047)に変換され、11bit相当の Resolution となる。
    • 上記の設定では、V = (3.3V/2017) * Digital = 0.0016121 * Digital で電圧が計算できる(LSB = 3.3/2017 = 1.6121mV)。
  • Samples averaged = 8 としたので、8回サンプリングした平均値または加算値が出力される。
  • Averaging mode = Fixed Resolution にすると、Samples averagedで設定したサンプリング数のデータが平均され、Accumulate にすると累算される。
adc_03.png
  • Sequenced channel でADCの入力ポート数を変更できる。
    • 回路図に示されているようにADCの入力に、アナログマルチプレクサが接続されており、多チャンネルの測定が可能になっている。1個のADCで複数チャネルを順次測定するため、チャネル間の同期はできない。
  • Mode欄で、入力をシングルまたは差動に設定できる。
  • Acquisition times で、チャネル毎にサンプル&ホールド回路(S/H)にかける時間数(クロック数)を設定できる。
    • 出力インピーダンス大きい信号源の場合は、S/H回路でサンプリングするために時間がかかるので Acquisition times を長めに設定する。
adc_04.png
  • Mode を Follower にすると、オペアンプがボルテージフォロワの結線となる。PSoC 4 にはパッシブ素子は含まれていないので、OpAmp?モードの場合は、外付け部品が必要となる。
adc_05.png
  • 以下の設定は、UARTの項で説明したのと同じ。
adc_06.png
adc_07.png
adc_08.png
  • main.c の記述
    • UARTから"DAC 1.0"のような文字列(strData)を受信すると、スペースの前後に分けて、コマンド(cmd)とパラメータ(par)に代入する。
      • 注意:文字列strDataをstrtokで分解すると、strDataのポインタが移動するので、次回の受信のために、strData, cmd, parの合計長さ分だけ初期化する。
      • UartGetChar?()関数は、文字列をNULL終端しないので、自分でNULL終端しておく。
    • ADCの結果を、そのチャネルの入力設定を用いて電圧値に変換する関数 CountsTo_Volts(チャネル番号, ADCデータ)が用意されている。
#include "project.h"
#include "stdlib.h"
#include "stdio.h"

int main(void)
{
    CyGlobalIntEnable; /* Enable global interrupts. */

    // Initiallization of UART module
    UART_1_Start();
    UART_1_UartPutString("ADC and DAC example with PSoC\r\n");
    uint32 rxData;
    int8 ptr;
    char strData[] = "\0";
    char *cmd = "\0";
    char *para = "\0";
    
    // IDAC valiables
    uint32 dacData = 0u;
    float32 vout = 0.0;

    // ADC valiables
    int16 adcData[2];
    // float32 vin[2];
    char adchex_0[6], adchex_1[6];      // 0x + 12bit + EOS
    // char adcv_0[16], adcv_1[16];
    
    // Initiallization of ADC, OPA, IDAC
    ADC_SAR_Seq_1_Start();
    Opamp_1_Start();
    IDAC_1_Start();
    IDAC_1_SetValue(dacData);
    
    for(;;)
    {
        // Checking command buffer and IDAC output
        rxData = UART_1_UartGetChar();
        if(rxData) {
            memset(strData, '\0', strlen(strData) + strlen(cmd) + strlen(para));
            ptr = 0;
            
            // Read until CR delimiter from RX buffer
            while(rxData != 0x0d) {
                if(rxData != 0x00) {
                    strData[ptr] = rxData;
                    ptr++;
                }
                rxData = UART_1_UartGetChar();
            }
            
            // NULL termination
            strData[strlen(strData)] = '\0';
            
            // Clear RX buffer and RX FIFO
            UART_1_SpiUartClearRxBuffer();
            
            // Detection of command and parameter token
            cmd = strtok(strData, " ");
            para = strtok(NULL, " ");

            // Decode of command
            if(!strcmp(cmd, "DAC") || !strcmp(cmd, "dac")) {
                // Calculation of digital input of DAC
                vout = strtof(para, NULL);
                if(vout > 3.06) vout = 3.06;  // Over range
                else if(vout < 0) vout = 0.0; // Under range
                dacData = (uint32)vout/0.012; // LSB = 12mV
                
                // Echo to UART
                UART_1_UartPutString(" DAC:  ");
                UART_1_UartPutString(cmd);
                UART_1_UartPutString(" ");
                UART_1_UartPutString(para);
                UART_1_UartPutString(" \r\n");
                
                // Output from IDAC
                IDAC_1_SetValue(dacData);
                
                // Software delay (ms) from DAC output to ADC start
                CyDelay(1u);
            }
            // Message for the unknown command
            else UART_1_UartPutString("Unknown command\r\n");
        }
        
        // ADC aquisiotion
        ADC_SAR_Seq_1_StartConvert(); // Trigger
        ADC_SAR_Seq_1_IsEndConversion(ADC_SAR_Seq_1_WAIT_FOR_RESULT); // Waiting
        
        // Getting the adc results
        adcData[0] = ADC_SAR_Seq_1_GetResult16(0);
        adcData[1] = ADC_SAR_Seq_1_GetResult16(1);
        
        // Conversion to voltage
        // vin[0] = ADC_SAR_Seq_1_CountsTo_Volts(0, ADC_SAR_Seq_1_GetResult16(0));
        // vin[1] = ADC_SAR_Seq_1_CountsTo_Volts(1, ADC_SAR_Seq_1_GetResult16(1));
        
        // Sending ADC results to UART
        snprintf(adchex_0, 6, "%#x", adcData[0]);
        snprintf(adchex_1, 6, "%#x", adcData[1]);
        // snprintf(adcv_0, 16, "%f", vin[0]);
        // snprintf(adcv_1, 16, "%f", vin[1]);
        UART_1_UartPutString("ADC1: ");
        UART_1_UartPutString(adchex_0);
        // UART_1_UartPutString("  ");
        // UART_1_UartPutString(adcv_0);
        UART_1_UartPutString("  ");  
        UART_1_UartPutString("ADC2: ");
        UART_1_UartPutString(adchex_1);
        // UART_1_UartPutString("  ");
        // UART_1_UartPutString(adcv_1);
        UART_1_UartPutString("\r\n");
        
        // Software delay (ms) until next aquisiotion
        CyDelay(1000u);
    }
}

GPIO割り込みとディープスリープ

GPIOピンにつないだスイッチによる、ディープスリープモード(または他の低消費電力モード)からの復帰の例。動作確認用にLEDを接続する。

int.jpg
int.png
  • Component Catalogより、Ports and Pins - Digital Input Pin, System - Interrupt, Ports and Pins - Digital Output Pin を各1個配置。配線は後で行う。
int_01.png
  • 各コンポーネントを下記のように設定。
  • Digital Input Pin の設定を行うと、irq端子が現れるので、isr_1 と接続する。
int_02.png
int_03.png
int_04.png
int_05.png
  • Workspace Explorer の Interrupts をダブルクリックし、Priority を設定。
    • これにより、割り込みの優先度が決まる(値が小さい方が優先)。ここでは、割り込み要因が1個しかないので、0 - 3 のどれでもよい。
int_06.png
  • main.c の記述
    • SW_Pinによる割り込み isr_1 とグローバル割り込みを有効にしてから、一旦、CySysPmDeepSleep?()命令によりディープスリープモードに遷移する。
    • SW_Pinによる割り込みが発生すると、ISR (Interrupt Service Routine) は、割り込み状態フラグを解除して終了する。割り込みによりディープスリープからウエイクアップするため、CySysPmDeepSleep?()命令以降の処理が実行され、その後、再びディープスリープモードに戻る。
#include "project.h"

// GPIO interrupt service routine
CY_ISR(sw_isr_led){
    // Clear pin interrupt
    SW_Pin_ClearInterrupt();
}

int main(void)
{
    // Assign the interrupt ve ctor
    isr_1_StartEx(sw_isr_led);
    
    CyGlobalIntEnable; /* Enable global interrupts. */

    for(;;)
    {    
        // Transition to deep sleep mode or sleep mode
        // CySysPmSleep();
        CySysPmDeepSleep();
        
        // Wakeup components
        // _Start(); _Wakeup();
    
        // Main process
        LED_Pin_Write(1);
        CyDelay(100u);
        LED_Pin_Write(0);
        
        // Sleep components (store the state before sleep)
        // _Stop(); _Sleep();
    }
}

WDT(タイマー)割り込みとディープスリープ

WDT (Watchdog Timer)によるディープスリープモード(またはスリープモード)からの復帰の例。回路は、GPIO割り込みの例と同じ。ディープスリープモードでは、ILO (Internal Low Speed Oscillator) は動作しているので、ILOを使ってWDTによりカウントを行い、WDT割り込みによりウエークアップ(アクティブモードに復帰)する。アクティブモードでの処理を終えると、再びディープスリープモードに遷移し、割り込みが発生するのを待つ。

int.png
  • Component Catalogより、System - Global Signal Reference を選んで、回路図に配置。勝手に、isr_1(Interruptモジュール)が付いてくる。
  • LEDの点灯で動作を確認するため、Ports and Pins - Digital Output Pin も配置。
wdt_01.png
  • Global Signal Reference, isr_1, Digital Output Pinを下記のように設定。
    • Global Signal Reference の Global Signal Name として、Watch Dog Timer interrupt を選択。
    • isr_1 の Interrupt Type は、DERIVED を選択。
      • Interrupt Typeでは、割り込みのソースに対して、レベルトリガー割り込みとエッジトリガー割り込みを選択できる。特に選択する必要が無ければ、DERIVED にしておくとソースの種類に応じて設定される。
wdt_02.png
wdt_03.png
wdt_04.png
  • Workspace Explorer の Interrupts をダブルクリックし、Priority を設定。
    • これにより、割り込みの優先度が決まる(値が小さい方が優先)。ここでは、割り込み要因が1個しかないので、0 - 3 のどれでもよい。
wdt_06.png
  • Workspace Explorer の Clocks をダブルクリックし、クロックソースの設定リストから、ILO の行をダブルクリック。
  • Configure System Clocks ウインドウが開くので、Timer(WDT) ISR欄で、User Provided を選択。こちらを選択しないと、ISR(割り込みサービスルーチン)が作成できない。
wdt_07.png
wdt_08.png
  • main.c の記述
    • WDT_INTERVAL にWDT(Watchdog Timer) に タイマー設定時間(ms)を設定、ILO_FREQ に ILO のクロック周波数(kHz)を設定する。
      • ILOは、ディープスリープモードでも動作し続ける。ILO の周波数は、説明書に、32kHz(周期T = 31.25us)と書かれているが、実際はやや高周波側にずれているようだ。
      • WDTのカウンターは、16bit (65,536カウント) なので、T = 31.25us とすると、最大のタイマー設定時間は、31.25 * 65536 = 2.048s になる。ここでは、ILO_FREQ * WDT_INTERVAL = 32kHz * 1000ms = 32,000カウントで割り込みするようWDTに設定している。
    • SLEEP_INTERVAL に、ウエークアップする周期(s)を設定する。
      • WDT は、約2秒までしかカウントできないので、SLEEP_INTERVALに設定した時間を超えるまで、ウエークアップ後の処理を実行せず、Deep sleep に戻る。
    • WDTによる割り込み isr_1 、グローバル割り込みを有効にしてから、一旦、CySysPmDeepSleep?()命令によりディープスリープモードに遷移する。
    • WDTによる割り込みが発生すると、ISR (Interrupt Service Routine) は、割り込みフラグを解除して終了する。WDT割り込みによりディープスリープからウエイクアップするため、CySysPmDeepSleep?()命令以降の処理が実行され、その後、再びディープスリープモードに戻る。
#include "project.h"

#define WDT_INTERVAL 1000 // (ms)
#define ILO_FREQ 32       // (kHz)
#define SLEEP_INTERVAL 5  // (s) for long interval over 2000ms

// WDT interrupt service routine
CY_ISR(wdt_isr_led){
    // Clear interrupt flag to enable next interrupt
    CySysWdtClearInterrupt(CY_SYS_WDT_COUNTER0_INT); 
}

int main(void)
{
    int16 sleep_time = 0; // Counter for the sleep interval
    
    // Initiallization of WDT
    // Counter 0 of Watchdog time generates peridically interrupt to wakeup system
    CySysWdtWriteMode(CY_SYS_WDT_COUNTER0, CY_SYS_WDT_MODE_INT);
    // Set interval as desired value
	CySysWdtWriteMatch(CY_SYS_WDT_COUNTER0, ((uint32)(WDT_INTERVAL * ILO_FREQ)));
    // clear counter on match event
	CySysWdtWriteClearOnMatch(CY_SYS_WDT_COUNTER0, 1u);
    
    // Enable watchdog
    // enable the counter 0
    CySysWdtEnable(CY_SYS_WDT_COUNTER0_MASK);
    // check if counter 0 is enabled, otherwise keep looping here
    while(!CySysWdtReadEnabledStatus(CY_SYS_WDT_COUNTER0));
    
    // Assign the interrupt vector
    isr_1_StartEx(wdt_isr_led);
    
    CyGlobalIntEnable; /* Enable global interrupts. */

    for(;;)
    {
        // Clear watchdog counter before deep sleep
        CySysWdtResetCounters(CY_SYS_WDT_COUNTER0_RESET);
        // reset watchdog counter requires several LFCLK cycles to take effect
        CyDelayUs(150u);
        
        // Transition to deep sleep mode or sleep mode
        CySysPmDeepSleep();
        
        if (sleep_time < SLEEP_INTERVAL) sleep_time++;
        else {
            // Wakeup components
            // _Start(); _Wakeup();

            // Main process
            LED_Pin_Write(1);
            CyDelay(100u);
            LED_Pin_Write(0);
        
            // Sleep components (store the state before sleep)
            // _Stop(); _Sleep();
            
            sleep_time = 0; // Reset counter
        }
    }
}

snprintf による float - ASCII 変換

PSoC Creator の snprintf, sprintf では、%f の処理ができないらしい(整数については問題なし)。以下のように対処する。snprintf の使用例は、ADCとIDACの項で紹介。

[参考] sprintf では、変換後の最大文字数を指定できないため、snprintfを使用したほうが安全。

  • 下記のディレクティブをmain.cに追加 [UARTデータシートを参照]。
    • floatを扱うライブラリが追加される。
  • Workspace Explorer の Systemをダブルクリック。
    • Heep Size を 0xffに変更してビルド。
      • デフォルトのヒープサイズ(0x80)では暴走するようなので、少し増やしてみる。ヒープ領域が大きいとPSoCのメモリが消費されるが、少ないと暴走するので、サイズの設定は状況に合わせて検討が必要。
/* Add an explicit reference to the floating point printf library to allow the usage of floating point conversion specifier */
#if defined (__GNUC__)
asm (".global _printf_float");
#endif


printf.png
トップ   新規 一覧 単語検索 最終更新   ヘルプ   最終更新のRSS