STM32 HAL DAC8555
https://www.ti.com/product/ja-jp/DAC8555
DAC8555にあるSYNCというピン、これは明らかにCS(チップセレクト)と読み替えて問題ない。HIHGの時には信号が入ってきても無視、LOWの時に動作。DINは命令がマイコン側から入ってくるピンで、SCLKは、名前の通りでクロック。テスト回路の作製
テストをするためにDAC8555の周辺回路をブレッドボードに形成する。DAC8555のマニュアルに記載のある推奨回路通りにしてみる。
GNDピン(6)とVrefL(5)を接続してGNDに接続。VrefとGNDの間に、0.1uFと10uFのコンデンサーを挟む。
SPIで利用するピンの確認のために、一旦IDEを開いて、Connectivity ^ SPI1にチェックをいれてModeをFull Duplex Masterを選択する。これでどのピンがSPIに使われるか把握する。
関数化
実際の物に配線を記載すると上のようになる。
上の図だとピン名称だけで実際のICのピンがどれかの対応が分かりにくいので、実際の物に配線を記載すると下のようになる。
同じように、AVddのピン(電源)まわりもパスコンを設置。
各ピンの役割は以下(マニュアルに書いてある内容の抜粋)
12ピン:IOVDD
ロジック回路の電圧。使っているSTM32は、3.3Vだが、5.0Vのロジックと混在する時などに使用する。ここではVccに接続する。
13ピン:RST
LOWの時リセットされて、ゼロかまたは、Vrefの中間の値の出力になる。
14ピン:RSTSEL
リセット時にゼロか、中間の値にするのかを選択する。中間にしたい場合はHIGHに接続、ゼロにしたい場合はGNDに接続。
15ピン:ENABLE:
読んで字のごとく。動作時にはLOWにする。HIGHだと動かない。
16ピン:LDAC:
後で動作の説明をするが、ソフト的にバッファーの値をアップデートし出力を変更できるが、このピンを使ってハードでも実施可能。LOWにしておく。
定電圧回路を準備する。今回は、テストでNJM12888の1.8V出力を用意した。
https://akizukidenshi.com/download/ds/njr/NJM12888_J.pdf
を用いてVrefを用意する。データシートに掲載の通りに電源回りにパスコンを設置する。
このレギュレーターは、コントロールがついていて、使わない時はOFFにする事もできる。ここではテストなので、コントロールは、Vccとつなげてしまって常時1.8V出力するようにする。
SPIの線をマイコンから接続する前は上記のようになる。DACのVrefHにジャンパーを指す前に所定の電圧1.8Vが出ているかをテスタで確認してからDACのVrefに接続する。
マイコンとの接続
SPIで利用するピンの確認のために、一旦IDEを開いて、Connectivity ^ SPI1にチェックをいれてModeをFull Duplex Masterを選択する。これでどのピンがSPIに使われるか把握する。
PA5: SCK
PA6: MISO (Master in slave out)
PA7: MOSI (Master out slave in)
DACでデータを返す事はしないので、MISOにつなぐ事は実際はない。
また、お隣のピンのPA4をGPIOでOutputに設定しておく。これはチップセレクトCSとして使う。(場合によってはSSと記載しているICもよく見る。Slave selectの略でSS)
SPIの通信はICをカスケード接続してSPIに接続する事ができる。その時、マイコンから投げた命令が、どのICに向かっているものか?というのを選定するのに、CSを使う。一般的にはCSがLOWのICが命令を受け取って実行し、その他のICはHIGHにしておくと命令が飛んできても無視するという算段。I2Cという通信規格は、アドレスをICがもっていて、命令を投げる時に、アドレスを指定する事で、ICの違いを判別する。
DAC8555のピン配置
SYNC:9
SCLK:10
DIN:11
なので、
PA4: CS ー SYNC:9ピン
PA5: SCK ー SCLK:10ピン
PA7:PA7: MOSI ― DIN:11ピン
を接続する事になる。
SPIタイミングチャート
この見方がわからないとSPI通信できない。
DINのデータが1か0か?を判定(サンプリング)するのは、このチャートによると、各クロックの立ち上がりで読む事がこの図からわかる。
ICによっては、立ち上がりだったりわかる。続いてわかる事は、CS(SYNC)がLOWになったら24番目の最上位にあるMSB(Most Significant Bit)DB23から順に送って最下位にあるビットLSB(Least Significant Bit)であるDB0を送り、その後CSピンを24ビット送信後(24ビット目のクロックの立下り後)にHIGHにすると、このチャートは読む。
CPOLビット:アイドル状態でのクロック信号の極性。アイドル状態は、送信を開始するためにCSがハイからローにCSがローからハイに遷移するまでの間
CPHAビット:クロックの立ち上がりか立下りかを選択。
CPOLはHIGHとLOWとどちらにも線が引いてある・・・・ん・・・結論はどちらでもよい。CSがLOWに落ちてその次の変化からクロックを読む事になる。
エッジはわかりやすく、必ずデータシートから読めるが、CPOLはよくわからない事がある。その時どうするか?両方やってみればはやいというのが現実解。
DAC8555
次にデータシートを見て、読み取るべき情報は、送付するデータ。
MSB側から
DB23:22 ゼロで固定
DB21:20 LD1とLD0の組み合わせで、動作を設定する。(動作は後ほど説明)
DB19 Xの部分はゼロでも1でもよく無視される。
DB18:17 DAC Select 1,0でチャンネルを指定する。
DB16 PD0:power-down modeの選択 PD0=1にするとpower-down modeに入るので、DB15,14の意味が変わる。
通常は、
DB15:0 の16ビットの部分で出力電圧の大きさを65536階調で指定する。1111111111111111が最も高い電圧でVrefと同じ電圧になって、0000000000000000がゼロの出力になる。
DB21:20の動作の所をもう少し、このADC、バッファー(一時的に値を格納するメモリ)をもっている。
00: 一つのチャンネルのバッファの値だけが個別に変更される。出力は00の間は変更されない。最後に、10にした時にすべてのチャンネルが同時に出力される。例えば、チャンネルA、B、Cは00でバッファーを書き換えるだけで、4チャンネル目(D)に、10とすると、チャンネル1~4が当時に出力される。
01の時は、一つ一つのチャンネルが、送った際に出力が個別に切り替わる。チャンネルA~Dを順にすれば、同時でなくて時間差がついてA~Dの順に出力がかわる。
コーディング
使う変数を定義する。
/* USER CODE BEGIN PV */
float Vset=1.2//目的の電圧
float Vdacref=1.800;//リファレンス電圧
uint8_t DACcontrol;//コントロールビット
uint16_t DataInt;//送信データ
uint8_t SPIbuffer[3];//SPI転送用のバッファー24ビット分
/* USER CODE END PV */
今回は、無限ループの外に記載をする。
/* USER CODE BEGIN 2 */
DACcontrol=0b00010000;//channel 1
DataInt=Vset/Vdacref*(65536-1);
SPIbuffer[0]=DACcontrol;
SPIbuffer[1]=DataInt >> 8;
SPIbuffer[2]=DataInt & 0x0F;
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_SET);
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_RESET);
HAL_SPI_Transmit(&hspi1, (uint8_t*)SPIbuffer, 3, 100);
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_SET);
/* USER CODE END 2 */
まずデータの準備。HALは8ビットか16ビット単位でデータを送り込む。今回はコントロールビット8ビット+データ16ビットの合計24ビット。あまりの出ない8ビットで3回送り込んでやる。
DACcontrol=0b00010000
bの後2つの00は、最初は00を送り込む決まりなので00。その次は、動作で01、つまり特定のピンのバッファーに値を書いて、すぐに出力する設定に。次の0は、0でも1でもよく無視される。次の00がチャンネル。チャンネルAを指定。最後がスリーブモードにするか?でしないので0。
DataInt=Vset/Vdacref*(65536-1);
出力電圧の設定。16ビット階調の整数で表示。FFの時に1.8Vが出る。
DataIntは16ビットで指定しているが、送り込む時は、8ビットづつになるので分割の処理。ビットシフトさせる。
例えば、
DataInt=1111 1010 0000 0101
を送り込む場合を考えてみる。
DataInt >> 8;
は右に8ビットよせ0000 0000 1111 1010
SPIbuffer[1]=1111 1010
MSBから8ビット分が入る事になる。
SPIbuffer[2]=DataInt & 0x0F;
の0x0Fとの論理和をとることになるので0000 0000 0000 0101
SPIbuffer[2]= 0000 0101
LSBの8ビットが入る。
データは用意できたのでSPIで送信する。
最初CSピンをHIGHにしておく、その後LOWにしてからデータを送り込む
HAL_SPI_Transmit(&hspi1, (uint8_t*)SPIbuffer, 3, 100);
第一引数:ハンドラーの構造体のポインタ
第二引数:データバッファへのポインタ
第三引数:送信するデータ量
第四引数:送信できない時のタイムアウト時間(msec指定)
最後に、CSピンをHIGHにして終了。
ちゃんと回路が形成できて、ちゃんとコーディングができていると、
チャンネルAの端子(ピン1)に所望の電圧である1.2Vが出力されているはずである。
テストのままでは、使い勝手が悪いので、変数の定義で、Vsetを削除またはコメントアウトする。下の方に行って、ユーザ関数を記載する。ここでは、第一引数にチャンネルの番号を、第二引数に出力したい電圧を入れたDAC8555という関数を定義する。
/* USER CODE BEGIN 4 */
void DAC8555(uint8_t Ch, float Vset0)
{
switch(Ch) {
case 0: //Channel A
DACcontrol = 0b00010000;
break;
case 1: //Channel B
DACcontrol = 0b00010010;
break;
case 2: //Channel C
DACcontrol = 0b00010100;
break;
case 3: //Channel D
DACcontrol = 0b00010110;
break;
default:
DACcontrol = 0b00010000;
}
DataInt=Vset0/Vdacref*(65536-1);
SPIbuffer[0]=DACcontrol;
SPIbuffer[1]=DataInt >> 8;
SPIbuffer[2]=DataInt & 0x0F;
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_SET);
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_RESET);
HAL_SPI_Transmit(&hspi1, (uint8_t*)SPIbuffer, 3, 100);
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_SET);
}
/* USER CODE END 4 */
チャンネルの違いは、switchで振り分けして、コントロールビットを変更する形とした。ビット操作でもできるはず。
上の方にもどって、プライベート関数の所に定義した関数を宣言する。
/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
static void MX_GPIO_Init(void);
static void MX_SPI1_Init(void);
void DAC8555(uint8_t Ch, float Vset0);
/* USER CODE BEGIN PFP */
今回もテストということで、whileの外側に
/* USER CODE BEGIN 2 */
DAC8555(0, 1.4);
DAC8555(1, 1.2);
DAC8555(2, 1.0);
DAC8555(3, 0.8);
/* USER CODE END 2 */
というコードを書いて、1.4Vから0.2V違う電圧がでるように4チャンネルを順に設定してみた。
ちゃんと動作していれば、チャンネルAには1.4V・・・と各出力が確認できる。
コメント
コメントを投稿