短縮URL https://bit.ly/2QWLHid
Cypress Semiconductor社PSoC Creator 関連のメモです。ここでは、PSoC4を使用しますが、PSoC3, PSoC5でも同じです。
コメントはありません。 Comments/PSoC Creator?
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などいろいろあって忘れそうなのでデータシートを要約。
PSoCでは、チップ全体の電力モードの他に、各回路モジュール毎の電源ON/OFFをプログラムで個別に切り替えることができる。全体の電源モードは、5段階用意されているが、定期的に動作させてデータを取得する場合は、スリープまたはディープスリープが使用できる。外部からの何らかのイベントで動作させる場合には、ハイバネートかストップが有用。ここでは、PSoC 4 の電力モードについて、データシートをまとめておく。
モード | 消費電力 | アクティブへの復帰方法 |
アクティブ | 1.3mA - 14mA | - |
スリープ | 1.0mA - 3mA | 任意の割り込み |
ディープスリープ | 1.3uA - 15uA | GPIO, WDTタイマー, I2C, コンパレータ, 外部リセットピン |
ハイバネート | 150nA - 1uA | GPIO, コンパレータ, 外部リセットピン |
ストップ | 20nA - 80nA | Wakeupピン, 外部リセットピン |
純正の書き込み器MiniProg3によるPSoC生チップのプログラムに関する覚え書き。もっとも、MiniProg3は値段が高いので、マニアには敬遠されているらしい。PSoC Programmerを起動して、Utilitiesタブからファームウエアをアップデートしておく(古いファームウエアだと、Power Cycleモードが使用できないと聞いている)。
学部生、卒研生向け、プログラムとデバックの手順解説。ここでは、CY8CKIT-142 PSoC4 BLEモジュール(CY8C4247LQI-BL483搭載、技適認証済み)を使用しましたが、BLEを使わないなら PSoC4 CY8C4245 SSOP版あたりも使いやすい。
PSoC Creatorのデバッグ機能は、非常に便利で、プログラム、動作確認、
小型キャラクタ液晶ディスプレイの使用例。3.3V動作、I2C接続の Xiamen Zettler Electronics社 AQM0802A-RN-GBW を使用。秋月でピッチ変換キット(AE-AQM0802)が販売されている。また、電源制御例を示す。LCDは低消費電力だが、1mA程度消費するので、小容量バッテリーや環境エネルギー駆動の場合、スイッチを押したときだけ表示し、しばらく表示したら電源を切る。電源は、PSoCのGNDとは分離されたCGNDラインに接続し、CGNDをPSoCのIO(オープンドレイン)でON/OFFする。VDDはON/OFFせず、パスコンに充電した状態を保つ。
#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); } } }
PCや無線モジュールとの標準的な接続方法の例。TXとRXは互いにクロスさせることに注意。プルアップ抵抗は、PSoCの入出力ピンで設定することもできるが、外部抵抗の使用が推奨されている。複数トークンを含む場合の受信処理は、ADCとIDACの項を参照。写真は、PCとの接続テストに使用した USB-Serial変換モジュール(スイッチサイエンス FTDI USBシリアル変換アダプター Rev.2)。
#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"); } } }
IDAC(Current Digital to Analog Converter)から出力した電圧をADC(SAR Analog to Digital Converter)で測定する例。電流出力のため、出力端子に接続した抵抗によって、出力電圧レンジが決定される。
#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ピンにつないだスイッチによる、ディープスリープモード(または他の低消費電力モード)からの復帰の例。動作確認用にLEDを接続する。
#include "project.h" // GPIO interrupt service routine CY_ISR(sw_isr_led){ // Clear pin interrupt SW_Pin_ClearInterrupt(); }
int main(void) { // Assign the interrupt vector 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 (Watchdog Timer)によるディープスリープモード(またはスリープモード)からの復帰の例。回路は、GPIO割り込みの例と同じ。ディープスリープモードでは、ILO (Internal Low Speed Oscillator) は動作しているので、ILOを使ってWDTによりカウントを行い、WDT割り込みによりウエークアップ(アクティブモードに復帰)する。アクティブモードでの処理を終えると、再びディープスリープモードに遷移し、割り込みが発生するのを待つ。
#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 } } }
PSoC Creator の snprintf, sprintf では、%f の処理ができないらしい(整数については問題なし)。以下のように対処する[UARTデータシート参照]。snprintf の使用例は、ADCとIDACの項で紹介。
[参考] sprintf では、変換後の最大文字数を指定できないため、snprintfを使用したほうが安全。
/* 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