STM32 HAL库实战(一)使用STM32驱动双通道12位DAC(TLV5618)

TLV5618参考资料

TLV5618是德州仪器公司研发的一种双通道12-bit数模转换器, 具有较宽的电压输出范围和2倍增益
本文参考资料:TLV5618 DataSheet

硬件资源概述

  • STM32F407ZGTx, 192KB RAM, 1MB Flash, 168MHz System Clock
  • TLV5618 SSOP-8 @Texas Instrucments

开发资源概述

  • IAR for ARM 8.10.0
  • STM32CubeMX v4.22 with STM32F4 HAL v1.16.0

使用低版本IAR for ARM将无法打开本工程文件

STM32F4 SPI分析

  1. SPI是Motorola开发的一种通用串行外设接口,支持发送16或8位的数据,使用者只需考虑初始时序即可
  2. SPI共有四根接线,分别是NSS,MISO,MOSI,SCLK
    • 使用SPI时,通信的双方分为主机和从机,一般认为有时钟信号输出的为主机,此次STM32作为主机
    • NSS,从器件选择,使用STM32时一般无需关心
    • MISO,主机输入,从机输出,是主机的数据输入线,同时也是从机的数据输出线
    • MOSI,与MISO相反,是从机的数据输入线,同时也是主机的数据输出线
    • SCLK,时钟信号线,与波特率有关
  3. 若不使用NSS线作为器件选择引脚,可以选用通用I/O接口作为片选(chip select),这么做的好处就是一个SPI接口可以挂载多个从器件,常见芯片多数为片选拉低有效
  4. STM32F4拥有三个相互独立的SPI接口,其中SPI1挂载在APB2总线(最高频率84MHz)上,SPI2和SPI3挂载在APB1总线(最高频率42MHz)上,此次选用SPI2为TLV5618的挂载接口
  5. 其余的请自行查阅STM32F4中文参考手册
    注:中文参考手册最新版为2013版,与最新版2017版英文手册有一定差距,最好查阅2017版手册

TLV5618数据手册分析

  1. SSOP-8封装引脚图(俯视图)
    STM32 HAL库实战(一)使用STM32驱动双通道12位DAC(TLV5618)_第1张图片
    • a. DIN 串行数据输入
      b. SCLK SPI时钟输入
      c. CS 片选引脚,低电平有效
      d. OUTA 通道A输出
      e. AGND 模拟/电源地
      f. REF 参考电压输入,此次选用2.048V参考电压(REF5020)
      g. OUTB 通道B输出
      h. VDD 供电端
  2. 电气参数及其特性此处不多赘述,只分析如何应用SPI驱动该芯片
  3. 根据数据手册,该芯片支持SPI和QSPI驱动,此次仅使用SPI,接线如下
    STM32 HAL库实战(一)使用STM32驱动双通道12位DAC(TLV5618)_第2张图片
  4. 芯片内部结构总览
    STM32 HAL库实战(一)使用STM32驱动双通道12位DAC(TLV5618)_第3张图片
  5. 芯片工作流程(此处是根据我的实践得出,不一定准确)

    • 单通道输出时,直接将数据写入相应通道并更新输出。假设此时令A通道输出2.048V,A通道执行完毕后对B通道进行操作,将不会影响A通道的值
    • 双通道同时输出时,允许输出不同的电压,但是仅允许如下工作模式:

    将通道B(即将输出的)的值先写入Buffer,然后写入通道A的值,最后一起更新AB的输出

  6. 时序图
    需要根据时序图确定SPI空闲时钟状态和采样边沿,如下
    STM32 HAL库实战(一)使用STM32驱动双通道12位DAC(TLV5618)_第4张图片

    • 由时序图可以看出,第一步应当拉低片选
    • 对于SCLK时钟,有两种选择
      (1) 时钟空闲状态为低,在第二个时钟边沿即第一个下降沿进行数据采样/发送数据,CPOL=0,CPHA=1
      (2) 时钟空闲状态为搞,在第一个时钟边沿即第一个下降沿进行数据采样/发送数据,CPOL=1,CPHA=0
    • 这次我使用了第二种选择
  7. 该芯片的输出计算公式
    Vout = 2 * (data / 2^12) * Vref
  8. 芯片对于SPI的波特率有要求,不得高于1.25MHz,如何设置波特率将在后文中提及
  9. 其他的一些小细节,也不赘述

STM32CubeMX配置

注:此处默认读者有一定的STM32 HAL库开发经验
1. 开启SPI2,选择片选引脚
STM32 HAL库实战(一)使用STM32驱动双通道12位DAC(TLV5618)_第5张图片
PB12为挂载在该SPI下的W25Q64(Flash)的片选引脚,可以忽略
2. 设置时钟树
STM32 HAL库实战(一)使用STM32驱动双通道12位DAC(TLV5618)_第6张图片
此处主频可以设置为168MHz,但是不能超过这个数值
3. SPI设置
STM32 HAL库实战(一)使用STM32驱动双通道12位DAC(TLV5618)_第7张图片
* 通过设置预分频系数改变波特率,这里使用了最低的波特率,但是只要波特率不大于1.25MHz,该芯片即可工作*
4.GPIO设置
STM32 HAL库实战(一)使用STM32驱动双通道12位DAC(TLV5618)_第8张图片
只需设置CS引脚即可
5.生成IAR工程即可

源码分析

声明:由于前日原工程丢失,故不提供工程文件下载,仅给出驱动源码,保持引脚一致,即可无损移植,或者,更改头文件中,位带操作的宏定义即可
1. 头文件

/**
 * Title    TI DAC (TLV5618) Driver Header Files
 * Refrence http://www.ti.com.cn/cn/lit/ds/symlink/tlv5618a.pdf
 * Author   Stark Zhang
 * Date     2017-07-18
 * Debug    Passed
**/

#ifndef __TLV5618_H
#define __TLV5618_H

#include "spi.h"
#include "main.h"
#include "stm32f4xx_hal.h"
#include "bsp.h"

//DAC Channel
#define Channel_A   0x01
#define Channel_B   0x02
#define Channel_AB  0x03

//Chip Select
#define TLV5618_CS  PBout(11)

//Voltage Refrence(Standard output)
#define REF5020     2.048
//Half of Integrated Counter 
#define Counter     2048

typedef struct
{
    uint8_t _channel;
    uint16_t _dataA;
    uint16_t _dataB;
    uint8_t _dataS[2];
    uint16_t _SPD;
    float _VA;
    float _VB;
    float _vref;
    uint16_t _counter;
}TLV5618_StatusTypeDef;

extern void TLV5618_init(void);
extern void TLV5618_deinit(void);
extern void TLV5618_DAConvert(TLV5618_StatusTypeDef *p);

#endif

//EOF

注意:此处使用位带操作声明了CS引脚,移植时仅需更改此处,另,在main函数调用void TLV5618_DAConvert(TLV5618_StatusTypeDef *p) 之前,需要首先声明全局结构体,稍后将给出示例
2. 源文件

/**
 * Title    TI DAC (TLV5618) Driver Header Files
 * Refrence http://www.ti.com.cn/cn/lit/ds/symlink/tlv5618a.pdf
 * Author   Stark Zhang
 * Date     2017-07-18
 * Debug    Passed
**/

/* Used Hardware SPI(3-Wire) to Drive DAC */

#include "TLV5618.h"

#define spi_da  hspi2

/**
 * @brief   Initialize DAC(maybe not used)
 * @func    void TLV5618_init()
 * @param   None
 * @return  None
 * @note    None
**/
void TLV5618_init(void)
{
    uint8_t data[2] = {0, 0};
    TLV5618_CS = 0;
    HAL_SPI_Transmit(&spi_da, data, 2, 5);
    TLV5618_CS = 1;
}

/**
 * @brief   Deinitialize DAC(maybe not used)
 * @func    void TLV5618_deinit()
 * @param   None
 * @return  None
 * @note    None
**/
void TLV5618_deinit(void)
{
    uint8_t data[2] = {0x20, 0};
    TLV5618_CS = 0;    
    HAL_SPI_Transmit(&spi_da, data, 2, 5);
    TLV5618_CS = 1;
}

/**
 * @brief   DAC convert function
 * @func    void TLV5618_DAConvert()
 * @param   TLV5618_StatusTypeDef *p
 * @return  None
 * @note    None
**/
void TLV5618_DAConvert(TLV5618_StatusTypeDef *p)
{
    TLV5618_CS = 0;
    p->_counter = Counter;
    p->_vref = REF5020;
    uint16_t SPD, Temp;
    if(p->_SPD == 1)
        SPD = 0x4000;
    else SPD = 0x0000;
    switch(p->_channel)
    {
    case Channel_A:
        Temp = (p->_dataA & 0x0FFF) | SPD | 0x8000;
        p->_dataS[0] = Temp >> 8 & 0x00FF;
        p->_dataS[1] = Temp & 0x00FF;
        HAL_SPI_Transmit(&spi_da, p->_dataS, 2, 5);
        p->_VA = p->_vref * p->_dataA / p->_counter + 0.001;
        break;
    case Channel_B:
        Temp = (p->_dataB & 0x0FFF) | SPD | 0x0000;
        p->_dataS[0] = Temp >> 8 & 0x00FF;
        p->_dataS[1] = Temp & 0x00FF;
        HAL_SPI_Transmit(&spi_da, p->_dataS, 2, 5);
        p->_VB = p->_vref * p->_dataB / p->_counter + 0.001;
        break;
    case Channel_AB:
        break;
    }
    TLV5618_CS = 1;
}

//EOF


  • void TLV5618_init(void) : 初始化TLV5618并对双通道清零
  • void TLV5618_deinit(void) : 强迫TLV5618进入掉电模式
  • void TLV5618_DAConvert(TLV5618_StatusTypeDef *p) : DA转换函数,理论上来说仅需要调用该函数即可,此外,AB双通道同时更新的工作模式我没有完成,如果有读者在我的基础上完成了,可以私信我,一起学习

main 函数示例一则
该函数仅作示例之用,并不是完整可用的正确函数
//省去include
#include ...

extern TLV5618_StatusTypeDef TLV5618_Status = {0};

int main(void)
{
    //STM32F4 初始化
    STM32_Init();
    //结构体赋值
    TLV5618_Status._channel = Channel_A;
    TLV5618_Status._dataA = 500;
    TLV5618_Status._dataB = 0;
    TLV5618_Status._SPD = 1;
    //DA转换
    TLV5618_DAConvert(&tLV5618_Status);

    while(1)
    {};
}

如上示例,该结构体仅需对以上四个成员变量进行赋值,同时,因为该结构体是全局的,因此可以直接通过该结构体了解DAC的状态,可能在使用上略有不便,各位读者可以自行修改。

全文完。

你可能感兴趣的:(STM32,C/C++)