アーカイブ
PSoC – CY8C29466の14ビットADコンバータを内蔵マルチプレクサで切り替えて複数ピンで使う
14ビットのADコンバータ1つと、AMUX4(4チャンネルアナログマルチプレクサ)を組み合わせると複数のピンからAD変換が使える。
というかPSoC Designer4.xでADCINC12を使っていた頃は、ADCINC12を複数置くことができたのにPSoC Designer5.0では1つしか配置できなくて困った。
でもよく考えたらマルチプレクサ使う方が自然だな。
アナログブロックの接続
マルチプレクサを切り替えるとPort0の0,2,4,6番ピンに接続できる

ピン配置
右上のADC以外は関係ない

PGAの設定
1.0倍で、AMUXがあるアナログマルチプレクサ1に接続する。

ADCINCの設定
Negative Inputのソースは一応指定するが、GainをDisconnectedにすると使わないようにできる。

AMUX4の設定
Interconnect Viewには表示されないけど、配置はされてる。アナログマルチプレクサ1として設定する。

AMUX4の他にも8もあるので、まあたくさん増やせる。
UARTのTX8で、14bitADCINCの値を送る。
TX8だけを使う設定は橋本商会 ≫ PSoC – TX8モジュールに書いた
void wait(int n){
while(n--);
}
// AMUX4_PORT0_0 => 0x00
// AMUX4_PORT0_2 => 0x01
// AMUX4_PORT0_4 => 0x02
// AMUX4_PORT0_6 => 0x03
int get_adc(BYTE amux_channel){
AMUX4_InputSelect(amux_channel);
wait(10);
ADCINC_GetSamples(0);
while(!ADCINC_fIsDataAvailable());
return ADCINC_iClearFlagGetData();
}
int ad;
BYTE ad_pin;
char buf[6];
void main(void){
M8C_EnableGInt;
AMUX4_Start();
PGA_1_Start(PGA_1_HIGHPOWER);
ADCINC_Start(ADCINC_HIGHPOWER);
TX8_Start(TX8_PARITY_NONE);
for(;;){
for(ad_pin = 0; ad_pin < 4; ad_pin++){
ad = get_adc(ad_pin);
TX8_PutChar(ad_pin+'0');
TX8_CPutString(":");
TX8_PutString(intToStr(ad,buf));
TX8_PutCRLF();
}
}
}
AMUX4を切り替えた直後にAD変換を開始するのがなんとなく嫌だった(物理的に回路が切り替わっているわけだし)ので、一瞬waitを入れてからAD変換するようにした。string.hのitoa関数がおかしいので代わりを作った
// intの桁数を返す
char getDigit(int n){
char i;
i = 0;
while(n>0){
n /= 10;
i++;
}
return i;
}
// int->String変換
// char buf[6]
char *intToStr(int n, char *buf){ // 変換する数、作業領域
int i, digit;
digit = getDigit(n); // 桁数
for(i = digit-1; i >= 0; i--){ // intは最大5桁
buf[i] = n%10+'0';
n /= 10;
}
buf[digit] = '\0'; // 行末
return buf;
}
PSoC CY8C29466のI2CHWモジュールで通信
PSoC CY8C29466のI2Cモジュールを試した。
I2Cは2本の信号線の上にmaster/slaveの2種類のノードを合計100個以上載せて相互に通信できるプロトコル。動作速度が違う部品同士をより少ない結線数で相互接続させる事を目的として、1992年にPhilipsが作って2004年に特許が切れている。今ではほとんどのマイコンにI2C通信をするモジュールがついているし、外部メモリやセンサーなどもI2Cで値を返す物がある。linuxのkernelにも入っていて(/usr/include/linux/i2c.hとか)マザーボード上の温度センサとかに使われているらしい。
このへんに詳しく書いてある。
- I2C通信の使い方
- IC – Wikipedia
- I2Cバス仕様書日本語版(pdf)
- 新連載 1、2、3線シリアル・インターフェース (連載 第1回) (エレキジャック)
- 1、2、3線シリアル・インターフェース (連載 第2回) (エレキジャック)
- User Module Datasheet: I2C Hardware Block Data Sheet, I2CHW – Cypress Semiconductor
プログラムはCypress公式のUser Module Datasheetのp.39,40ものを参考にした。
■作った物
PSoC Designer 5.0+SP6で作ったプロジェクトまるごとgithubに置いた。
git clone git://github.com/shokai/i2c-uart-cy8c29466.gitで取得できる。
3つ並んでいるPSoCマイコンのうち一番下がmasterで、上2つがslaveデバイス。デバイスが接続されている2つの線は2kΩの抵抗(1kΩ2つ)を介してVCCと接続されpull upされている。I2Cの接続構成とプルアップ抵抗に適切なプルアップ抵抗の計算方法が書かれている。電源5Vで100kbpsか400kbpsなら2kΩ〜5kΩが最適値とのこと。

masterに付いているボタンを押すと、2つのslaveにLED点灯命令を送る。ボタンを離すと消灯する。

slave側のボタンは、それぞれmasterの2つのLEDの点灯/消灯に対応している。


masterはslaveをコントロールするだけでなく、I2Cとシリアル通信のブリッジになっている。
slaveからのデータをslaveのアドレス名と共にUARTでパソコンに中継する。また、パソコンからmasterマイコンに’U'か’D'の文字を送る事と、masterは全slaveにLED点灯/消灯を送る。
UARTとRS232Cのレベル変換にはADM3202を使った。
だいたいそういう内容のビデオ
■デバイス設定
master側。

パソコンとのシリアル通信で9600bpsを作るためにVC1,VC3を設定している。動作電圧やCPUクロックなどはデフォルト値。

single masterモードでモジュールを置いた。
100kbpsでPORT1の5番と7番ピンをI2Cに使う。bufferはプログラム中で自分で用意したBYTE配列を使ってもらうように設定。

シリアル通信で9600bpsが出るようにUARTモジュールを設定。
橋本商会 PSoC – CY8C29466でUART受信割り込みでくわしく書いた。

3つのLEDに使うピンは出力をStrongにして3V出るようにした。
タクトスイッチにつなぐPort2の2番ピンは両エッジ入力割り込みを設定しつつ内部でプルアップして、ブレッドボード上でも100Ωの抵抗でプルダウンする。こうするとチャタリングが起きない。

ピン配置図

アナログブロックは使っていないので上の方だけ。
UARTがPort2の4,5番ピンから出るように引き出した。
slave側。

Global Resourceは初期値のまま

I2CHWモジュールををslaveで置いた。速度はmasterに合わせる。
slaveデバイスのアドレスは7bitだが、0×00~0×10までの16個のアドレスはプロトコルに予約されているので、0×11からの112個が使える。
今回はmasterのプログラムで0×11~0×20のデバイスがいるか確認しながら動作するようにしているので、アドレス17~32のどれかを使えばいい。

masterと同じ様にLEDとタクトスイッチ用のピンを設定。LEDは1つだけ。


なにも結線していない
■プログラム
master側
slaveアドレス0×11から0×20までのデバイスに、順にTXバッファを書き込み、RXバッファに読み込む。送受信を待っている間、timeout_countをカウントアップし続けて、閾値以上になるとタイムアウトするようにしている。これで存在するかわからないslaveとやりとりできるし、動作中にslaveデバイスが増えても問題なくネットワークに参加させられる。
UARTの受信やピン入力の検出は割り込みで処理している(橋本商会 PSoC – CY8C29466でUART受信割り込みで書いた)のだが、I2Cはメインループの中で処理している。I/OからのイベントでUARTぐらいなら使ってもいいけど、16個のslaveとのやりとりを割り込みの中でやると多重割り込みが起こりやすいので、各ルーチンからbufferを読み書きしてそれを定期的にslaveと共有するという方式にした。
本番ではdigital blockを1つ消費するだけで使える8bit Timerモジュールで定期的に回すようにすると良いと思う。
main.c
// I2C-UART master
// CY8C29466-24PXI(DIP Package)
// PSoC Designer 5.0 + SP6
// IMAGECRAFT C Compiler
#include <m8c.h> // part specific constants and macros
#include <I2CHW_1Common.h>
#include <I2CHW_1Mstr.h>
#include "PSoCAPI.h" // PSoC API definitions for all User Modules
#define _BV(BIT) (1<<BIT)
#define sbi(BYTE,BIT) (BYTE |= _BV(BIT))
#define cbi(BYTE,BIT) (BYTE &= ~_BV(BIT))
#define LED_ON() sbi(PRT2DR, 0) // LED
#define LED_OFF() cbi(PRT2DR, 0)
#define LED2_ON() sbi(PRT1DR, 6)
#define LED2_OFF() cbi(PRT1DR, 6)
#define LED3_ON() sbi(PRT1DR, 4)
#define LED3_OFF() cbi(PRT1DR, 4)
#define BTN_PORT PRT2DR // push button
#define BTN_BIT _BV(2)
#define BUF_SIZE 8
BYTE buf_tx[BUF_SIZE]; // I2C buffer
BYTE buf_rx[BUF_SIZE];
BYTE status; // I2C status
BYTE slave; // slave address
#define I2C_TIMEOUT 128 // 長時間応答が返ってこないslaveデバイスを無視する
BYTE timeout_count, i;
void main(void)
{
M8C_EnableGInt; // enable global interrupt
M8C_EnableIntMask(INT_MSK0, INT_MSK0_GPIO);
UART_1_CmdReset(); // uart init
UART_1_IntCntl(UART_1_ENABLE_RX_INT); // enable receive interrupt
UART_1_Start(UART_1_PARITY_NONE);
LED_ON();
UART_1_CPutString("start");
LED_OFF();
I2CHW_1_Start();
I2CHW_1_EnableMstr();
I2CHW_1_EnableInt();
for(;;){
for(slave = 0x11; slave < 0x21; slave++){
I2CHW_1_bWriteBytes(slave, buf_tx, BUF_SIZE, I2CHW_1_CompleteXfer); // master->slave
timeout_count = 0;
for(;;){
if(I2CHW_1_bReadI2CStatus() & I2CHW_WR_COMPLETE ||
timeout_count++ > I2C_TIMEOUT) break;
}
I2CHW_1_ClrWrStatus();
I2CHW_1_fReadBytes(slave, buf_rx, BUF_SIZE, I2CHW_1_CompleteXfer); // slave->master
timeout_count = 0;
for(;;){
if(I2CHW_1_bReadI2CStatus() & I2CHW_RD_COMPLETE ||
timeout_count++ > I2C_TIMEOUT) break;
}
I2CHW_1_ClrRdStatus();
switch(slave){
case 0x11:
if(buf_rx[0] == 'u') LED2_OFF();
else if(buf_rx[0] == 'd') LED2_ON();
break;
case 0x12:
if(buf_rx[0] == 'u') LED3_OFF();
else if(buf_rx[0] == 'd') LED3_ON();
break;
}
while(!(UART_1_bReadTxStatus() & UART_1_TX_BUFFER_EMPTY)); // slaveからの受信データをシリアル通信出力
UART_1_CPutString("I2C:");
UART_1_PutSHexByte(slave); // slaveアドレス
UART_1_CPutString(",");
UART_1_PutString(buf_rx); // slaveからの受信データ
UART_1_PutCRLF();
for(i = 0; i < BUF_SIZE-1; i++) buf_rx[i] = '\0'; // 受信バッファを初期化
}
}
}
// UART受信割り込み
#pragma interrupt_handler INT_UART_RX
void INT_UART_RX(void){
char recv_data;
recv_data = UART_1_cGetChar(); // read UART
UART_1_PutChar(recv_data); // echo
switch(recv_data){
case 'U':
LED_ON();
buf_tx[0] = 'A'; // slaveにLED点灯を指示
UART_1_CPutString("LED:ON\r\n");
break;
case 'D':
LED_OFF();
buf_tx[0] = 'B'; // slaveにLED消灯を指示
UART_1_CPutString("LED:OFF\r\n");
break;
}
}
// I/Oピン状態変化割り込み
#pragma interrupt_handler INT_GPIO
void INT_GPIO(void){
if(BTN_PORT & BTN_BIT){ // ボタンが押されている時
LED_ON();
buf_tx[0] = 'A'; // slaveにLED点灯を指示
UART_1_CPutString("LED:ON\r\n");
}
else{
LED_OFF();
buf_tx[0] = 'B'; // slaveにLED消灯を指示
UART_1_CPutString("LED:OFF\r\n");
}
}
slave側
slaveもmasterと同じ様にglobalにバッファを用意して、そこに適当に現在の状態を読み書きし、定期的にメインループ内でmasterと通信し共有している。この方が素直に書きやすい。
LEDとボタンの状態を結びつける情報を1byte目に置いているが、それ以外の情報をやりとりする場合もシリアライズとかして通信するのではなく2byte目以降を使う方がいい。あくまで必要な変数を定期的に同期させられる、分散オブジェクト風に書いた方がすっきりする(PSoCのstring.hがおかしいという理由もあるけど)
あと、ややこしいのがslaveなのでI2CHW_1_InitWrite関数を呼ぶとmasterからslaveへの書き込みが起こる。受信する。送信ではない。また、I2CHW_1_bReadI2CStatus() での状態チェックも、dataに使っている信号線1本の状態のチェックのためなので呼び出すタイミングがmasterと逆になる。このへんややこしいのであまり触りたくないから、通信は隔離された別ループでやってglobalに置いた共用bufferを同期させるという方法にした。
main.c
// I2C-UART slave
// CY8C29466-24PXI(DIP Package)
// PSoC Designer 5.0 + SP6
// IMAGECRAFT C Compiler
#include <m8c.h> // part specific constants and macros
#include <I2CHW_1Common.h>
#include "PSoCAPI.h" // PSoC API definitions for all User Modules
#define _BV(BIT) (1<<BIT)
#define sbi(BYTE,BIT) (BYTE |= _BV(BIT))
#define cbi(BYTE,BIT) (BYTE &= ~_BV(BIT))
#define LED_ON() sbi(PRT2DR, 0) // LED
#define LED_OFF() cbi(PRT2DR, 0)
#define BTN_PORT PRT2DR // push button
#define BTN_BIT _BV(2)
#define BUF_SIZE 8
BYTE buf_rx[BUF_SIZE]; // I2C buffer
BYTE buf_tx[BUF_SIZE] = {'x'};
BYTE status; // I2C status
void main(void)
{
M8C_EnableGInt;
M8C_EnableIntMask(INT_MSK0, INT_MSK0_GPIO);
I2CHW_1_Start();
I2CHW_1_EnableSlave();
I2CHW_1_EnableInt();
for(;;){
status = I2CHW_1_bReadI2CStatus();
if(status & I2CHW_WR_COMPLETE){ // master->slave
I2CHW_1_ClrWrStatus();
I2CHW_1_InitWrite(buf_rx, BUF_SIZE);
}
if(status & I2CHW_RD_COMPLETE){ // slave->master
I2CHW_1_ClrRdStatus();
I2CHW_1_InitRamRead(buf_tx, BUF_SIZE);
}
if(buf_rx[0] == 'A') LED_ON(); // masterからの指示でLEDの点灯/消灯を切り替え
else if(buf_rx[0] == 'B') LED_OFF();
}
}
#pragma interrupt_handler INT_GPIO
void INT_GPIO(void){
if(BTN_PORT & BTN_BIT){ // ボタンを押している時
buf_tx[0] = 'd'; // 押下をmasterに通知
}
else{
buf_tx[0] = 'u';
}
}
PSoC – TX8モジュール
UARTモジュールとまるきり同じだけど、TXだけ使うとデジタルブロック1つで済むので節約したい時に使える。
とくにCY8C21334でCapSense (CSD)使うと、digital/analogともに残り1ブロックになるので、このTX8を使うかI2Cでもう1つ別のマイコンとやりとりしてそっちにシリアル通信してもらうかしないとパソコンとやりとりできない。
9600bps出すためのCPU設定。
内蔵24MHz → System Clock → VC1 → VC3と流れて分周していってTX8_1のクロックソースにする

Port_0_1まで結線する

main.c
#include <m8c.h> // part specific constants and macrosこれだけで8bit パリティ無し ストップビット1で送信できる。
#include "PSoCAPI.h" // PSoC API definitions for all User Modules
void main()
{
TX8_1_Start(TX8_1_PARITY_NONE);
while(1){
TX8_1_CPutString("this is test¥r¥n");
}
}
あとは秋月のFT232RL基盤のRXDピンに流せばいい
→ 橋本商会 秋月FT232RL基盤をPSoC CY8C29466で使ってみる
受信できた

PSoC CapSenseで静電容量タッチセンサ 4つ動かした
前の記事の回路などが思いきり間違っていたので修正しつつ、CapSenseとLED4つずつに増やした。
多分、マイコンの足の数だ増やせる。
例によってコードはBitbucketにアップした
CapSense CSDウィザードで4つスイッチを置いて

AnalogMUXBusに接続されている事を確認

CSDの設定。しきい値などをかなり何度も直した。適当にやってたので理論をわかってないんだけど、どうやらセンサ部分の形状や配置に応じて微調整しなければ使い物にならないっぽい。

ピンの設定。LEDが4つにした。
使ってないピンをPull Upにしておくとノイズが来ないので安定する事に気づいた。

実体配置図

前の記事の回路が間違っていて、あのままではうまく動かなかったが、1kΩの抵抗と0.1μFのキャパシタを足したら複数認識できるようになった。
CapSense CSDのデータシートにあった図。抵抗とキャパシタが必要らしい

P1[5]とP0[3]の間に1kΩを入れて、P0[3]からキャパシタを通して銅箔テープに延ばす。

前の記事はP1[5]とP0[3]で静電容量取ろうとしてたので、思いっきり間違ってた。
プログラム。
前のやつのif(CSD_1_bIsSensorActive(センサ番号))を増やしただけ。
shokai / CapSense LED / source — bitbucket.org
main.c
#include <m8c.h> // part specific constants and macros
#include "PSoCAPI.h" // PSoC API definitions for all User Modules
#define _BV(BIT) (1<<BIT)
#define sbi(BYTE,BIT) (BYTE |= _BV(BIT))
#define cbi(BYTE,BIT) (BYTE &= ~_BV(BIT))
#define LED_0_ON() sbi(PRT1DR, 7);
#define LED_0_OFF() cbi(PRT1DR, 7);
#define LED_1_ON() sbi(PRT0DR, 1);
#define LED_1_OFF() cbi(PRT0DR, 1);
#define LED_2_ON() sbi(PRT0DR, 5);
#define LED_2_OFF() cbi(PRT0DR, 5);
#define LED_3_ON() sbi(PRT0DR, 7);
#define LED_3_OFF() cbi(PRT0DR, 7);
void main()
{
M8C_EnableGInt;
CSD_1_Start();
CSD_1_InitializeBaselines();
CSD_1_SetDefaultFingerThresholds();
while(1){
CSD_1_ScanAllSensors();
CSD_1_UpdateAllBaselines();
if(CSD_1_bIsSensorActive(0)){
LED_0_ON();
}
else{
LED_0_OFF();
}
if(CSD_1_bIsSensorActive(1)){
LED_1_ON();
}
else{
LED_1_OFF();
}
if(CSD_1_bIsSensorActive(2)){
LED_2_ON();
}
else{
LED_2_OFF();
}
if(CSD_1_bIsSensorActive(3)){
LED_3_ON();
}
else{
LED_3_OFF();
}
}
}
PSoC CapSenseで静電容量タッチセンサを自作した
回路などが間違っている事に気がつきました
正しい実装は次の記事へ。
使った部品の説明などはこのまま下へ。
*********************************************
できたっぽい。ひさしぶりにマイコンいじった。
銅箔テープの部分に指が近づけると電気容量の乱れるので、それをPSoC CY8C21334のCapSense機能で検出してLEDを光らせている。
コードもbitbucketにアップしました
(音小さい)
LEDは点滅させたくてさせているんじゃないんだけど、してしまっている。
もう少し抵抗値などの調整が必要らしい。
静電容量タッチセンサはメカニカルな部品が無いので、ビデオの最後にやっている様に、何の変哲もない壁に埋め込んでその上に人の手が乗っているかを検出したりできる。
防水加工も容易だし、銅箔等を使えば曲面に埋め込む事も出来る。
実際ヨーロッパの寒いところに行くと信号機やエレベータのボタンが凍結防止のために静電容量センサで実装されていたりする。
PSoCのCapSenseはワンチップで静電容量センサを簡単に実装できるパッケージで、商業的にはSONYのオーディオコンポやAppleのiPodのホイールなんかにも使われていてずいぶん潤っているらしく、セミナーも無料だし基盤は配るしタダメシも出てくる。
次は複数のcapsenseを使えるようにしてみる予定。

■必要な部品
リンク先は買ったお店の商品ページ。
- PSoCマイコン CY8C21334-24PVXI(20pin)
capsenseがついているマイコン。いろいろ探した結果、これをchip1stopで買うのがコストパフォーマンスが一番良いという結論になった - 秋月のSSOP変換基板
CY8C21334がSSOPという超小さいパッケージしか無いので、ブレッドボードに刺せるように足幅を広げる基盤。28ピンなので8ピン余るけど安いからこれを使う。 - 銅箔テープ たしか千石で買ったと思う。薄くて平べったい電極ならなんでも良さそう
あとは、秋月で買ったPSoC MiniProgというUSB接続の書き込み機と、ブレッドボードとLEDとクリップとベニヤ板と変換基板のピンヘッダ(オス)。
■センサー部分の制作
銅箔テープを千石で買ってきて適当な木の板に平行に貼る。裏に糊がついていたので楽だった。


CY3235 CapSense-ProxDetでは2.2kΩの抵抗と0.1μFのキャパシタがついているらしいが、手持ちのPSoC Expressセミナーの時にもらったCY3121では何もなくCY8C21434に直結しているように見えるので、これでいいのかも。
■マイコンのはんだづけ
今回はんだづけはここだけ。
フラックスをたっぷり塗って、足に沿ってさっさっと撫でる。


■PSoC Designerでのデバイス設定
新規プロジェクトをCY8C21334 24PVXI(20pin)で開始して、
Device EditorでUser ModulesからCap Sense(CSD)を配置。

「Select CSD with clock prescaler」を選択した。
PSoC入門:PSoC First Touch編 – MeRLマイコンブの記事ではwithout clock prescalerを選んでいるが、digital/analog block消費量も変わらないのでclock prescalerにしてみた。
Interconnect Viewで、CSD_1をplaceしてから右クリックすると「CSDウィザード」というのがあるのでそれを使う。
センサの数を1、スライダーを0にしてPORT0_1を左上にドラッグアンドドロップして「OK」

すると、CSD_1に接続されている。
いままでUARTやADCモジュールではInterconnect Viewで自分で接続していたが、その必要が無くなっている。

CSD_1の設定を確認。Pinの設定をしておく。

最後に、PORT0_0でLEDを光らせるためにDriveをStrongに変更

ピン割り当て

Generate Applicationして、Cでプログラムを書く。
■PSoC Designerでのプログラミング
shokai / CapSense LED / source — bitbucket.org
main.c
#include <m8c.h> // part specific constants and macros
#include "PSoCAPI.h" // PSoC API definitions for all User Modules
#define _BV(BIT) (1<<BIT)
#define sbi(BYTE,BIT) (BYTE |= _BV(BIT))
#define cbi(BYTE,BIT) (BYTE &= ~_BV(BIT))
#define LED_ON() sbi(PRT0DR, 0);
#define LED_OFF() cbi(PRT0DR, 0);
void main()
{
M8C_EnableGInt;
CSD_1_Start();
CSD_1_InitializeBaselines();
CSD_1_SetDefaultFingerThresholds();
while(1){
CSD_1_ScanAllSensors();
CSD_1_UpdateAllBaselines();
if(CSD_1_bIsSensorActive(0)){
LED_ON();
}
else{
LED_OFF();
}
}
}
■マイコンの回路を作る
データシートを見て、PSoC MiniProgから接続が必要なSCLK、SDATA、XRES、VDD(VCC)、VSS(GND)の位置を確認

その通りに配線。電源はUSBのPSoC MiniProgから給電できる。
この時点でLEDを点灯させるだけのプログラムを書いて、マイコン単体で動くか確認した。

さきほどのプログラムを書き込んで、
PORT0_1とPORT1_5からクリップで銅箔テープに接続して完成。
さわると光る。



最近のコメント