STM32 HAL DAC8555





 https://www.ti.com/product/ja-jp/DAC8555

DAC8555にあるSYNCというピン、これは明らかにCS(チップセレクト)と読み替えて問題ない。HIHGの時には信号が入ってきても無視、LOWの時に動作。DINは命令がマイコン側から入ってくるピンで、SCLKは、名前の通りでクロック。


テスト回路の作製

テストをするためにDAC8555の周辺回路をブレッドボードに形成する。DAC8555のマニュアルに記載のある推奨回路通りにしてみる。

実際の物に配線を記載すると上のようになる。

上の図だとピン名称だけで実際のICのピンがどれかの対応が分かりにくいので、実際の物に配線を記載すると下のようになる。


GNDピン(6)とVrefL(5)を接続してGNDに接続。VrefとGNDの間に、0.1uFと10uFのコンデンサーを挟む。
同じように、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ピン
を接続する事になる。

DAC出力は、外付けのテスタで電圧を確認するとして、電子回路の方は一旦完成。


SPIタイミングチャート

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ビット:クロックの立ち上がりか立下りかを選択。

DAC8555の場合はCPHAは立下りのエッジを見るので2Edgeを選択しておく。
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・・・と各出力が確認できる。


コメント

このブログの人気の投稿

Attiny85とAQM0802A(LCD)のI2C接続

CH9329で日本語キーボード109で正しく表示する方法