×
单片机 > 单片机程序设计 > 详情

基于STM32的DAC8760 菊花链驱动

发布时间:2020-05-26 发布时间:
|

环境:STM32F103RC,主频 72MHz(外部晶振)或64MHz(HSI)


          两块DAC8760菊花链链接,采用SPI1驱动


note:发生电流回路开路等时,ALARM脚持续低,当电流回路等正常,DAC芯片能自动恢复正常,故程序无需特殊处理。


代码实现:


//DAC8760.c

 

#define DAC8760_GLOBALS

#include "DAC8760.h"

 

static const uint8_t ORange[6] = 

{

    0, // 0~5V(OFF)

    0, // 1~5V

    1, // 0~10V

    3, // -10~10V

    6, // 0~20mA

    5, // 4~20mA

}

 

/******************************************************

函数名称:WriteSingle_DAC8760

函数描述:单块DAC的写函数

Calls:HAL_SPI_TransmitReceive

Called by: Init_DAC8760

输入参数: ch_Addr:写操作地址

ch_Data:数据

返回值:HAL status

******************************************************/  

static HAL_StatusTypeDef WriteSingle_DAC8760(uint8_t ch_Addr, uint16_t ch_Data)

{

HAL_StatusTypeDef status;

    uint8_t liv_Data[3]; // 0: ch_Addr  1:ch_Datadata高8位 2:ch_Datadata低8位

 


uint8_t liv_RXData[3] = {0};


liv_Data[0] = ch_Addr;

liv_Data[1] = (ch_Data & 0xff00) >> 8;

liv_Data[2] = ch_Data & 0x00ff;


DAC8760_LATCH_0;

status = HAL_SPI_TransmitReceive(&hSPI1, liv_Data, liv_RXData, 3, TIMEOUT); 

DAC8760_LATCH_1;


delay_us(1);

return status;

}

 

/******************************************************

函数名称:Write_DAC8760

函数描述:2块DAC菊花链的写函数

Calls:HAL_SPI_TransmitReceive

Called by: EnableOutputControl、ChangeOutputRange等

输入参数: ch1_Addr:通道1写操作地址

ch1_Data:通道1的数据

ch2_Addr:通道2写操作地址

ch2_Data:通道2的数据

返回值:HAL status

******************************************************/

static HAL_StatusTypeDef Write_DAC8760(uint8_t ch1_Addr, uint16_t ch1_Data, uint8_t ch2_Addr, uint16_t ch2_Data)

{

HAL_StatusTypeDef status;

    uint8_t liv_Data[6]; // 0: ch2_Addr  1:ch2_Datadata高8位 2:ch2_Datadata低8位

// 0: ch1_Addr  1:ch1_Datadata高8位 2:ch1_Datadata低8位


uint8_t liv_RXData[6] = {0};


liv_Data[0] = ch2_Addr;

liv_Data[1] = (ch2_Data & 0xff00) >> 8;

liv_Data[2] = ch2_Data & 0x00ff;


liv_Data[3] = ch1_Addr;

liv_Data[4] = (ch1_Data & 0xff00) >> 8;

liv_Data[5] = ch1_Data & 0x00ff;


DAC8760_LATCH_0;

status = HAL_SPI_TransmitReceive(&hSPI1, liv_Data, liv_RXData, 6, TIMEOUT); 

DAC8760_LATCH_1;


delay_us(1);

return status;

}

 

/******************************************************

函数名称:Read_DAC8760

函数描述:2块DAC菊花链的读函数

Calls:HAL_SPI_TransmitReceive

Called by: EnableOutputControl、ChangeOutputRange等

输入参数: ch1_Addr:通道1读的寄存器地址

ch2_Addr:通道2读的寄存器地址

data: data[0]--Ch1 data;data[1]--Ch2 data

返回值:HAL status

******************************************************/

static HAL_StatusTypeDef Read_DAC8760(uint8_t ch1_Addr, uint8_t ch2_Addr, uint16_t *data)

{

HAL_StatusTypeDef status;

uint8_t liv_TXData[6]; // 0: ch2_Addr  1:ch2_Datadata高8位 2:ch2_Datadata低8位

// 0: ch1_Addr  1:ch1_Datadata高8位 2:ch1_Datadata低8位

uint8_t liv_RXData[6];


liv_TXData[0] = W_ADDR_READ;

liv_TXData[1] = (ch2_Addr & 0xff00) >> 8;

liv_TXData[2] = ch2_Addr & 0x00ff;


liv_TXData[3] = W_ADDR_READ;

liv_TXData[4] = (ch1_Addr & 0xff00) >> 8;

liv_TXData[5] = ch1_Addr & 0x00ff;


DAC8760_LATCH_0;

status = HAL_SPI_TransmitReceive(&hSPI1, liv_TXData, liv_RXData, 6, TIMEOUT); 

DAC8760_LATCH_1;

 

delay_us(1);

// if (status == HAL_OK)

// {

// *(data + 1) = (liv_RXData[4] << 8) | liv_RXData[5];

// }


liv_TXData[0] = W_ADDR_NOP;

liv_TXData[1] = 0;

liv_TXData[2] = 0;


liv_TXData[3] = W_ADDR_NOP;

liv_TXData[4] = 0;

liv_TXData[5] = 0;


DAC8760_LATCH_0;

status = HAL_SPI_TransmitReceive(&hSPI1, liv_TXData, liv_RXData, 6, TIMEOUT); 

DAC8760_LATCH_1;

delay_us(1);


if (status == HAL_OK)

{

*data = (liv_RXData[1] << 8) | liv_RXData[2];

*(data + 1) = (liv_RXData[4] << 8) | liv_RXData[5];  // 2018.9.18修改

}


return status;

}

 

/******************************************************

函数名称:EnableOutputControl

函数描述:输出使能控制

Calls:Read_DAC8760、Write_DAC8760

Called by: Clear2RoadsOutput、Init_DAC8760

输入参数:ch1Enable:1--使能输出;0--禁能

  ch2Enable:1--使能输出;0--禁能

返回值:无

******************************************************/

static void EnableOutputControl(uint8_t ch1Enable, uint8_t ch2Enable)

{

#ifdef READ_CTRL

uint16_t oldStatus[2], newStatus[2];

#endif


assert_param((ch1Enable <= 1) && (ch2Enable <= 1));

 

#ifdef READ_CTRL

if (Read_DAC8760(R_ADDR_CTRL, R_ADDR_CTRL, oldStatus) == HAL_OK)

{

oldStatus[0] &= ~(CTRL_OUTEN);

oldStatus[1] &= ~(CTRL_OUTEN);


newStatus[0] = oldStatus[0] | SET_OUTEN(ch1Enable);

newStatus[1] = oldStatus[1] | SET_OUTEN(ch2Enable);


Write_DAC8760(W_ADDR_CTRL, newStatus[0], W_ADDR_CTRL, newStatus[1]);

}

#else

if (ch1Enable == 1)

{

gUCTRL_Value.usCTRL1Value |= SET_OUTEN;

}

else

{

gUCTRL_Value.usCTRL1Value &= ~SET_OUTEN;

}


if (ch2Enable == 1)

{

gUCTRL_Value.usCTRL2Value |= SET_OUTEN;

}

else

{

gUCTRL_Value.usCTRL2Value &= ~SET_OUTEN;

}


Write_DAC8760(W_ADDR_CTRL, gUCTRL_Value.usCTRL1Value, W_ADDR_CTRL, gUCTRL_Value.usCTRL2Value);

#endif


}

 

/******************************************************

函数名称:Clear2RoadsOutput

函数描述:使两通道的输出为0

Calls:EnableOutputControl

Called by: Init_DAC8760

输入参数:无

返回值:无

******************************************************/

static void Clear2RoadsOutput(void)

{

EnableOutputControl(OUTPUT_DISENABLE, OUTPUT_DISENABLE);

DAC8760_CLR_1;

delay_us(6);

DAC8760_CLR_0;

}

 

/******************************************************

函数名称:ChangeOutputRange

函数描述:改变输出范围,并且当需要将量程改成(1~5V)或(0~5V)时进行一些处理

Calls:Read_DAC8760、Write_DAC8760、SetCh1DACData、SetCh2DACData

Called by: sDoIntSomething

输入参数:pUHoldingReg: 传入gUHoldingReg共用体指针         

  roadFlag:需要修改量程的通道

  bkpFlag:BKP备份标志

返回值:无

******************************************************/

void ChangeOutputRange(UHoldingReg *pUHoldingReg, uint32_t roadFlag, SFlag *pSFlag, uint32_t bkpFlag)

{

if (bkpFlag == TRUE)

{

// 备份

gUBKPData.CH1_AOUT_Range = pUHoldingReg->CH1_AOUT_Range;

gUBKPData.CH2_AOUT_Range = pUHoldingReg->CH2_AOUT_Range;

gSFlag.Write_BKP_Flag = TRUE;

}


#ifdef READ_CTRL

uint8_t ch1Range = ORange[pUHoldingReg->CH1_AOUT_Range];

uint8_t ch2Range = ORange[pUHoldingReg->CH2_AOUT_Range];

 

uint16_t oldRange[2], newRange[2];


Read_DAC8760(R_ADDR_CTRL, R_ADDR_CTRL, oldRange);


oldRange[0] &= ~(CTRL_RANGE);

oldRange[1] &= ~(CTRL_RANGE);


newRange[0] = oldRange[0] | SET_RANGE(ch1Range);

newRange[1] = oldRange[1] | SET_RANGE(ch2Range);


Write_DAC8760(W_ADDR_CTRL, newRange[0], W_ADDR_CTRL, newRange[1]);


#else

gUCTRL_Value.usCTRL1Value &= (ORange[pUHoldingReg->CH1_AOUT_Range] | 0xFFF8);

gUCTRL_Value.usCTRL1Value |= ORange[pUHoldingReg->CH1_AOUT_Range];


gUCTRL_Value.usCTRL2Value &= (ORange[pUHoldingReg->CH2_AOUT_Range] | 0xFFF8);

gUCTRL_Value.usCTRL2Value |= ORange[pUHoldingReg->CH2_AOUT_Range];


Write_DAC8760(W_ADDR_CTRL, gUCTRL_Value.usCTRL1Value, W_ADDR_CTRL, gUCTRL_Value.usCTRL2Value);

#endif


// 当用户选择量程为1~5V,需要软件将输出设为1V

switch (roadFlag)

{

case CH1AOUT_RANGE_ADD:

{

pUHoldingReg->Ch1_AOUT_Value = 0;

if (pUHoldingReg->CH1_AOUT_Range == VORANGE_1_5)

{

SetCh1DACData(0x3333);

pUHoldingReg->Ch1_AOUT_Value = 1;

}

else if(pUHoldingReg->CH1_AOUT_Range == VORANGE_0_5)

{

SetCh1DACData(0);

}

else if (pUHoldingReg->CH1_AOUT_Range ==IORANGE_4_20)

{

pUHoldingReg->Ch1_AOUT_Value = 4;

}


pSFlag->Aout1Rang_ChangeFlag = TRUE;

break;

}

case CH2AOUT_RANGE_ADD:

{

pUHoldingReg->Ch2_AOUT_Value = 0;

if (pUHoldingReg->CH2_AOUT_Range == VORANGE_1_5)

{

SetCh2DACData(0x3333);

pUHoldingReg->Ch2_AOUT_Value = 1;

}

else if(pUHoldingReg->CH2_AOUT_Range == VORANGE_0_5)

{

SetCh2DACData(0);

}

else if (pUHoldingReg->CH2_AOUT_Range ==IORANGE_4_20)

{

pUHoldingReg->Ch2_AOUT_Value = 4;

}


pSFlag->Aout2Rang_ChangeFlag = TRUE;

break;

}

default:

break;

}


}

 

/******************************************************

函数名称:SetCh1DACData

函数描述:设置通道1DAC输出,并立即更新。

Calls:Write_DAC8760

Called by: ChangeOutputRange、sDoFloatSomething

输入参数:dacValue: 0~65535

返回值:无

******************************************************/

void SetCh1DACData(uint16_t dacValue)

{

Write_DAC8760(W_ADDR_DATA, dacValue, W_ADDR_NOP, 0);

}

 

/******************************************************

函数名称:SetCh2DACData

函数描述:设置通道2DAC输出,并立即更新。

Calls:Write_DAC8760

Called by: ChangeOutputRange、sDoFloatSomething

输入参数:dacValue: 0~65535

返回值:无

******************************************************/

void SetCh2DACData(uint16_t dacValue)

{

Write_DAC8760(W_ADDR_NOP, 0, W_ADDR_DATA, dacValue);

}

 

/******************************************************

函数名称:Init_DAC8760

函数描述:SPI1和DAC8760的初始化代码

Calls:WriteSingle_DAC8760、Write_DAC8760、Clear2RoadsOutput等

Called by: main

输入参数:hspi:传入SPI_HandleTypeDef结构体指针

pUHoldingReg:传入gUHoldingReg共用体指针 

pUCTRL_Value:用来存放2块DAC芯片的控制寄存器值

返回值:无

******************************************************/

void Init_DAC8760(SPI_HandleTypeDef *hspi, UHoldingReg *pUHoldingReg, UCTRL_Value *pUCTRL_Value)

{


/**********************SPI1模块的初始化代码,配置成主机模式******************/

__HAL_RCC_SPI1_CLK_ENABLE();


hspi->Instance = SPI1;

hspi->Init.Mode = SPI_MODE_MASTER; // 主模式

hspi->Init.Direction = SPI_DIRECTION_2LINES; // 双线模式

hspi->Init.DataSize = SPI_DATASIZE_8BIT; // 发送接收8位帧结构

hspi->Init.CLKPolarity = SPI_POLARITY_LOW; // 串行同步时钟空闲状态为低电平

hspi->Init.CLKPhase = SPI_PHASE_1EDGE; // 串行同步时钟的第1个跳变沿(上升沿)数据被采样

hspi->Init.NSS =  SPI_NSS_SOFT; // SS信号由硬件(NSS管脚)还是软件控制

hspi->Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_8; // SPI波特率预分频值 18M/8

hspi->Init.FirstBit = SPI_FIRSTBIT_MSB;

hspi->Init.TIMode = SPI_TIMODE_DISABLE;

hspi->Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE; // 关闭硬件CRC

hspi->Init.CRCPolynomial = 7; // CRC值计算的多项式

HAL_SPI_Init(hspi);


__HAL_SPI_ENABLE(hspi);

/****************************************************************************/


/**********************DAC8760初始化部分*************************************/

WriteSingle_DAC8760(W_ADDR_RESET, SOFTWARE_RESET);

 

pUCTRL_Value->usCTRL1Value |= CTRL_DCEN;

WriteSingle_DAC8760(W_ADDR_CTRL, CTRL_DCEN);


Write_DAC8760(W_ADDR_NOP, 0, W_ADDR_RESET, SOFTWARE_RESET);


pUCTRL_Value->usCTRL2Value |= CTRL_DCEN;

Write_DAC8760(W_ADDR_NOP, 0, W_ADDR_CTRL, CTRL_DCEN ); // 菊花链使能

 

pUCTRL_Value->usCTRL1Value &= (ORange[pUHoldingReg->CH1_AOUT_Range] | 0xFFF8);

pUCTRL_Value->usCTRL1Value |= ORange[pUHoldingReg->CH1_AOUT_Range];


pUCTRL_Value->usCTRL2Value &= (ORange[pUHoldingReg->CH2_AOUT_Range] | 0xFFF8);

pUCTRL_Value->usCTRL2Value |= ORange[pUHoldingReg->CH2_AOUT_Range];


Write_DAC8760(W_ADDR_CTRL, pUCTRL_Value->usCTRL1Value, W_ADDR_CTRL, pUCTRL_Value->usCTRL2Value); // 菊花链使能和输出范围设置


Clear2RoadsOutput();

EnableOutputControl(TRUE, TRUE);

                                                                                                                                                                                                                                               

/****************************************************************************/

}

// DAC8760.h

ifndef _DAC8760_H

#define _DAC8760_H

 

#ifdef DAC8760_GLOBALS

#define DAC8760_EXT

#else

#define DAC8760_EXT extern 

#endif

 

#include "global.h"

 

#define DAC8760_CLR_0  HAL_GPIO_WritePin(GPIOC, GPIO_PIN_14, GPIO_PIN_RESET)

#define DAC8760_CLR_1  HAL_GPIO_WritePin(GPIOC, GPIO_PIN_14, GPIO_PIN_SET)

#define DAC8760_LATCH_0 HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_RESET)

#define DAC8760_LATCH_1 HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_SET)

#define ALARM_PORT_STATUS()     HAL_GPIO_ReadPin(GPIOC, GPIO_PIN_13)

 

/* 通道定义 */

#define AOUT_CH1 1

#define AOUT_CH2 2

 

/* DAC8760 8位地址 */

#define W_ADDR_NOP           0x00

#define W_ADDR_DATA          0x01 // 写数据寄存器

#define W_ADDR_READ          0x02 // 读

#define W_ADDR_CTRL          0x55 // 写控制寄存器

#define W_ADDR_RESET         0x56 // 写复位寄存器

#define W_ADDR_CONFIG        0x57 // 写配置寄存器

#define W_ADDR_GAIN          0x58 // 写增益校准寄存器

#define W_ADDR_ZERO          0x59 // 写零校准寄存器

#define W_ADDR_WDT           0x95 // 写看门狗寄存器

 

/* 读操作命令寻址的寄存器地址 */ 

#define R_ADDR_SATUS       0x00

#define R_ADDR_DATA        0x01

#define R_ADDR_CTRL        0x02

#define R_ADDR_CONFIG      0x0B

#define R_ADDR_GAIN        0x13

#define R_ADDR_ZERO        0x17

 

/* 寄存器值宏定义 */

#define SOFTWARE_RESET    1  

#define CTRL_DCEN          (1 << 3) // 菊花链使能

#define CTRL_OUTEN          (1 << 12)

#define CTRL_RANGE          (7 << 0)

#define TIMEOUT             500

 

#define OUTPUT_ENABLE       1

#define OUTPUT_DISENABLE    0

 

//#define READ_CTRL

#ifdef READ_CTRL

#define SET_OUTEN(x)        (x << 12)

#else

#define SET_OUTEN        (1 << 12)

#endif

#define SET_RANGE(x)        (x & 0x07)

 

#pragma anon_unions

typedef union

{

uint16_t usCTRLValue[2];

struct

{

uint16_t usCTRL1Value;

uint16_t usCTRL2Value;

};

}UCTRL_Value;

 

#pragma no_anon_unions

 

DAC8760_EXT SPI_HandleTypeDef hSPI1;

DAC8760_EXT UCTRL_Value gUCTRL_Value;

 

void ChangeOutputRange(UHoldingReg *pUHoldingReg, uint32_t roadFlag, SFlag *pSFlag, uint32_t bkpFlag);

void SetDACData(uint16_t dac1Value, uint16_t dac2Value);

void SetCh1DACData(uint16_t dacValue);

void SetCh2DACData(uint16_t dacValue);

void Init_DAC8760(SPI_HandleTypeDef *hspi, UHoldingReg *pUHoldingReg, UCTRL_Value *pUCTRL_Value);

#endif

 

关键字:DAC8760  菊花链驱动

『本文转载自网络,版权归原作者所有,如有侵权请联系删除』

热门文章 更多
ARM 汇编的必知必会