短縮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)で測定する例。IDACは、電流出力のため、出力端子に接続した抵抗によって、出力電圧レンジが決定される。
#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 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 (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 の処理ができないらしい(整数については問題なし)。以下のように対処する。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