一、简介
8 位 DAC5311、10 位 DAC6311 和 12 位 DAC7311 (DACx311) 是低功耗、单通道、电压输出数模转换器 (DAC)。DACx311 在正常工作状态下具有低功耗(5V 时为 0.55mW,断电模式下可降至 2.5μW),使其成为便携式电池供电应用的理想选择。
器件信息:DAC7311系列芯片手册
器件型号 | 分辨率 |
---|---|
DAC7311 | 12位 |
DAC6311 | 10位 |
DAC5311 | 8位 |
驱动电路:
二、驱动程序
本文主要使用模拟IIC来进行通信,所以其他的驱动程序可以不用打开,只需要GPIO引脚的驱动程序,软件生成的工程是默认打开引脚驱动,只需要编写模拟IIC的时序即可。
1.DAC7311选择
DAC的输出结果根据以下公式进行计算,其中参数含义如下:
- n:每位的分辨率:8(DAC5311)、10(DAC6311)或12(DAC7311)
- D:加载到DAC寄存器的二进制代码的十进制等值值。D的范围为8位DAC5311为0到255,10位DAC6311为0到1023,12位DAC7311为0到4095。
- AVdd:是带边芯片的供电电压。
2.DAC7311时序图
通过时钟、SYN和DIN的时序来编写发送数据的函数即可。
3.模式选择
DACx311包含四种独立的操作模式。这些模式可以通过在控制寄存器中设置两个位(PD1和PD0)来进行编程。表8-1显示了位的状态如何对应于设备的操作模式,通常我们设置为空模式
。
4.数据寄存器
DB15和DB14两位代表模式设置,为00;DB0-DB11代表发送的数据;DB0和DB1则不使用,设置为00。
三、DAC设置注册
将DAC芯片注册为一个DAC设备,注册设备的函数如下,并且在函数中需要编写对应的功能,在调用的使用进行设备的初始化,然后调用写函数来发送数据,进行DAC输出电压设置。
/*=====================================================##### 设备注册 #####==================================================*/
/**
* @brief DAC设备初始化
* @param dev:设备
*/
static rt_err_t DAC7311_Init(rt_device_t dev)
{
dac_device_t *user = (dac_device_t *)dev->user_data;
user->dev_name = DAC7311_DEV_NAME;
user->scl = DAC7311_SCL_PIN;
user->syn = DAC7311_SYN_PIN;
user->sda = DAC7311_SDA_PIN;
user->delay_us = 10;
rt_pin_mode(user->scl, PIN_MODE_OUTPUT);
rt_pin_mode(user->syn, PIN_MODE_OUTPUT);
rt_pin_mode(user->sda, PIN_MODE_OUTPUT);
rt_pin_write(user->syn, PIN_HIGH);
rt_pin_write(user->scl, PIN_HIGH);
rt_pin_write(user->sda, PIN_LOW);
return RT_EOK;
}
/**
* @brief 设备关闭
* @param dev:设备
* @param oflag:打开方式
*/
static rt_err_t DAC7311_Open(rt_device_t dev, rt_uint16_t oflag)
{
return RT_EOK;
}
/**
* @brief 设备关闭
* @param dev:设备
*/
static rt_err_t DAC7311_Close(rt_device_t dev)
{
return RT_EOK;
}
/**
* @brief 设备读取
* @param dev:设备
* @param pos:读取的位置
* @param buffer:数据缓冲区
* @param size:缓冲区的大小
*/
static rt_size_t DAC7311_Read(rt_device_t dev, rt_off_t pos, void *buffer, rt_size_t size)
{
return RT_EOK;
}
/**
* @brief 设备写入
* @param dev:设备
* @param pos:写入的位置
* @param buffer:写入的数据缓冲区
* @param size:缓冲区大小
*/
static rt_size_t DAC7311_Write(rt_device_t dev, rt_off_t pos, const void *buffer, rt_size_t size)
{
uint16_t *data = (uint16_t *)buffer;
rt_size_t ret = DAC7311_Write_Data((dac_device_t *)dev->user_data, *data);
return ret;
}
/**
* @brief 设备控制
* @param dev:设备
* @param cmd:指令
* @param args:命令参数
*/
static rt_err_t DAC7311_Control(rt_device_t dev, int cmd, void *args)
{
dac_device_t *cfg = (dac_device_t *)dev->user_data;
int ret = -RT_ERROR;
switch(cmd)
{
case RT_DEVICE_CTRL_LOCK:
{
lock(cfg);
ret = RT_EOK;
break;
}
case RT_DEVICE_CTRL_UNLOCK:
{
unlock(cfg);
ret = RT_EOK;
break;
}
default:
{
ret = -RT_ERROR;
break;
}
}
return ret;
}
/**
* @brief DAC设备注册
* @return 返回注册结果
*/
static int DAC7311_Device_Register(void)
{
rt_device_t dac_dev = &(dac_device_config.base_device);
// 初始化互斥量
rt_err_t ret = rt_mutex_init(&dac_device_config.dac_lock, DAC7311_DEV_NAME, RT_IPC_FLAG_PRIO);
RT_ASSERT(ret == RT_EOK);
// 设置设备类型和设备标志
dac_dev->type = RT_Device_Class_SPIDevice; // 设备类型--SPI设备
dac_dev->rx_indicate = RT_NULL;
dac_dev->tx_complete = RT_NULL;
// 设置设备操作接口
dac_dev->init = DAC7311_Init; // 设备初始化
dac_dev->open = DAC7311_Open; // 设备打开
dac_dev->close = DAC7311_Close; // 设备关闭
dac_dev->read = DAC7311_Read; // 设备读取
dac_dev->write = DAC7311_Write; // 设备写入
dac_dev->control = DAC7311_Control; // 设备控制
dac_dev->user_data = &dac_device_config; // 用户数据
// 注册设备
ret = rt_device_register(dac_dev, DAC7311_DEV_NAME, RT_DEVICE_FLAG_RDWR);
if (ret != RT_EOK)
{
LOG_E("Failed to register DAC7311 device");
return -RT_ERROR;
}
return RT_EOK;
}
INIT_DEVICE_EXPORT(DAC7311_Device_Register);
/*=====================================================####### END #######=================================================*/
四、完整代码
1.dac7311.h
#ifndef APPLICATIONS_DAC7311_H_
#define APPLICATIONS_DAC7311_H_
#include <drv_common.h>
#include <stdlib.h>
#include <rtdef.h>
#define DBG_TAG "main"
#define DBG_LVL DBG_LOG
#include <rtdbg.h>
/**====================================================###### 宏定义 ######==================================================*/
#define DAC7311_DEV_NAME "dac7311"
#define DAC7311_MODE 0x3FFF // 选择模式为Normal
#define DAC7311_SCL_PIN GET_PIN(F, 14) // SCL引脚
#define DAC7311_SYN_PIN GET_PIN(F, 2) // SYN引脚
#define DAC7311_SDA_PIN GET_PIN(F, 15) // SDA引脚
#define RT_DEVICE_CTRL_LOCK 0x13 // 上锁
#define RT_DEVICE_CTRL_UNLOCK 0x14 // 解锁
//#define lock(x) rt_mutex_take(x, RT_WAITING_FOREVER) // 上锁
//#define unlock(x) rt_mutex_release(x) // 销毁
#define lock(x) rt_mutex_take(&x->dac_lock, RT_WAITING_FOREVER)
#define unlock(x) rt_mutex_release(&x->dac_lock)
typedef struct
{
struct rt_device base_device; // 继承 rt_device 结构体
struct rt_mutex dac_lock; // 设备互斥量
rt_uint8_t scl; // 时钟线
rt_uint8_t sda; // 数据线
rt_uint8_t syn; // 片选
rt_uint32_t delay_us; // 延时
const char *dev_name; // 设备名称
} dac_device_t;
dac_device_t dac_device_config; // 设备信息
rt_device_t dac_device; // DAC设备
extern void DAC_Device_Init(void);
extern void write_dac_data(uint16_t data);
/**====================================================####### END #######=================================================*/
#endif /* APPLICATIONS_DAC7311_H_ */
2.dac7311.c
#include "dac7311.h"
/*=====================================================### 静态函数调用 ###==================================================*/
/**
* @brief DAC延时
* @param us:延时时间
*/
static void DAC7311_Delay(uint8_t us)
{
for (; us != 0; us--);
}
/**
* @brief 设置SYN状态
* @param data:用户数据
* @param state:状态
*/
static void set_syn(void *data, rt_int32_t state)
{
dac_device_t *cfg = (dac_device_t *)data;
if (state)
{
rt_pin_write(cfg->syn, PIN_HIGH);
}
else
{
rt_pin_write(cfg->syn, PIN_LOW);
}
}
/**
* @brief 设置SDA状态
* @param data:用户数据
* @param state:状态
*/
static void set_sda(void *data, rt_int32_t state)
{
dac_device_t *cfg = (dac_device_t *)data;
if (state)
{
rt_pin_write(cfg->sda, PIN_HIGH);
}
else
{
rt_pin_write(cfg->sda, PIN_LOW);
}
}
/**
* @brief 设置SCL状态
* @param data:用户数据
* @param state:状态
*/
static void set_scl(void *data, rt_int32_t state)
{
dac_device_t *cfg = (dac_device_t *)data;
if (state)
{
rt_pin_write(cfg->scl, PIN_HIGH);
}
else
{
rt_pin_write(cfg->scl, PIN_LOW);
}
}
/**
* @brief DAC写数据
* @param data:需要写入的数据
*/
static rt_size_t DAC7311_Write_Data(void *conf, uint16_t data)
{
dac_device_t *cfg = (dac_device_t *)conf;
uint16_t temp = data << 2; // 7311需要移动两位
temp &= DAC7311_MODE;
set_syn(conf, 1);
set_scl(conf, 1);
DAC7311_Delay(cfg->delay_us);
set_syn(conf, 0);
DAC7311_Delay(cfg->delay_us);
for (int i = 0; i < 16; ++i)
{
set_scl(conf, 1);
if (0x8000 == (temp & 0x8000))
{
set_sda(conf, 1);
}
else
{
set_sda(conf, 0);
}
DAC7311_Delay(cfg->delay_us);
set_scl(conf, 0);
DAC7311_Delay(cfg->delay_us);
temp <<= 1;
}
set_syn(conf, 1);
rt_size_t ret = sizeof(uint16_t);
return ret;
}
/*=====================================================####### END #######=================================================*/
/*=====================================================##### 设备注册 #####==================================================*/
/**
* @brief DAC设备初始化
* @param dev:设备
*/
static rt_err_t DAC7311_Init(rt_device_t dev)
{
dac_device_t *user = (dac_device_t *)dev->user_data;
user->dev_name = DAC7311_DEV_NAME;
user->scl = DAC7311_SCL_PIN;
user->syn = DAC7311_SYN_PIN;
user->sda = DAC7311_SDA_PIN;
user->delay_us = 10;
rt_pin_mode(user->scl, PIN_MODE_OUTPUT);
rt_pin_mode(user->syn, PIN_MODE_OUTPUT);
rt_pin_mode(user->sda, PIN_MODE_OUTPUT);
rt_pin_write(user->syn, PIN_HIGH);
rt_pin_write(user->scl, PIN_HIGH);
rt_pin_write(user->sda, PIN_LOW);
return RT_EOK;
}
/**
* @brief 设备关闭
* @param dev:设备
* @param oflag:打开方式
*/
static rt_err_t DAC7311_Open(rt_device_t dev, rt_uint16_t oflag)
{
return RT_EOK;
}
/**
* @brief 设备关闭
* @param dev:设备
*/
static rt_err_t DAC7311_Close(rt_device_t dev)
{
return RT_EOK;
}
/**
* @brief 设备读取
* @param dev:设备
* @param pos:读取的位置
* @param buffer:数据缓冲区
* @param size:缓冲区的大小
*/
static rt_size_t DAC7311_Read(rt_device_t dev, rt_off_t pos, void *buffer, rt_size_t size)
{
return RT_EOK;
}
/**
* @brief 设备写入
* @param dev:设备
* @param pos:写入的位置
* @param buffer:写入的数据缓冲区
* @param size:缓冲区大小
*/
static rt_size_t DAC7311_Write(rt_device_t dev, rt_off_t pos, const void *buffer, rt_size_t size)
{
uint16_t *data = (uint16_t *)buffer;
rt_size_t ret = DAC7311_Write_Data((dac_device_t *)dev->user_data, *data);
return ret;
}
/**
* @brief 设备控制
* @param dev:设备
* @param cmd:指令
* @param args:命令参数
*/
static rt_err_t DAC7311_Control(rt_device_t dev, int cmd, void *args)
{
dac_device_t *cfg = (dac_device_t *)dev->user_data;
int ret = -RT_ERROR;
switch(cmd)
{
case RT_DEVICE_CTRL_LOCK:
{
lock(cfg);
ret = RT_EOK;
break;
}
case RT_DEVICE_CTRL_UNLOCK:
{
unlock(cfg);
ret = RT_EOK;
break;
}
default:
{
ret = -RT_ERROR;
break;
}
}
return ret;
}
/**
* @brief DAC设备注册
* @return 返回注册结果
*/
static int DAC7311_Device_Register(void)
{
rt_device_t dac_dev = &(dac_device_config.base_device);
// 初始化互斥量
rt_err_t ret = rt_mutex_init(&dac_device_config.dac_lock, DAC7311_DEV_NAME, RT_IPC_FLAG_PRIO);
RT_ASSERT(ret == RT_EOK);
// 设置设备类型和设备标志
dac_dev->type = RT_Device_Class_SPIDevice; // 设备类型--SPI设备
dac_dev->rx_indicate = RT_NULL;
dac_dev->tx_complete = RT_NULL;
// 设置设备操作接口
dac_dev->init = DAC7311_Init; // 设备初始化
dac_dev->open = DAC7311_Open; // 设备打开
dac_dev->close = DAC7311_Close; // 设备关闭
dac_dev->read = DAC7311_Read; // 设备读取
dac_dev->write = DAC7311_Write; // 设备写入
dac_dev->control = DAC7311_Control; // 设备控制
dac_dev->user_data = &dac_device_config; // 用户数据
// 注册设备
ret = rt_device_register(dac_dev, DAC7311_DEV_NAME, RT_DEVICE_FLAG_RDWR);
if (ret != RT_EOK)
{
LOG_E("Failed to register DAC7311 device");
return -RT_ERROR;
}
return RT_EOK;
}
INIT_DEVICE_EXPORT(DAC7311_Device_Register);
/*=====================================================####### END #######=================================================*/
/*=====================================================##### 外部调用 #####==================================================*/
/**
* @brief 设备初始化
*/
void DAC_Device_Init(void)
{
dac_device = rt_device_find(DAC7311_DEV_NAME);
RT_ASSERT(dac_device != RT_NULL);
rt_err_t ret = rt_device_open(dac_device, RT_DEVICE_FLAG_RDWR);
RT_ASSERT(ret == RT_EOK);
ret = rt_device_init(dac_device);
RT_ASSERT(ret == RT_EOK);
}
/**
* @brief 写DAC值
* @param data:写入的数据
*/
void write_dac_data(uint16_t data)
{
rt_device_control(dac_device, RT_DEVICE_CTRL_LOCK, RT_NULL);
rt_device_write(dac_device, 0, &data, sizeof(uint16_t));
rt_device_control(dac_device, RT_DEVICE_CTRL_UNLOCK, RT_NULL);
}
/**
* @brief 指令设置DAC的值
* @param argc
* @param argv
* @return
*/
static int DAC_Param_Set(int argc, char **argv)
{
RT_ASSERT(argc == 2);
write_dac_data(atoi(argv[1]));
return RT_EOK;
}
MSH_CMD_EXPORT_ALIAS(DAC_Param_Set, dac, DAC Param Set);
/*=====================================================####### END #######=================================================*/
3.main.c
#include <rtthread.h>
#include <drv_common.h>
#define DBG_TAG "main"
#define DBG_LVL DBG_LOG
#include <rtdbg.h>
#include "dac7311.h"
/* 心跳灯线程的入口函数 */
static void thread_heartbeat_entry(void *parameter)
{
int count = 1;
while (1)
{
count++;
rt_pin_write(GET_PIN(E, 12), count % 2);
rt_thread_mdelay(1000);
}
}
/* 创建心跳灯线程 */
static int thread_heartbeat(void)
{
rt_pin_mode(GET_PIN(E, 12), PIN_MODE_OUTPUT);
/* 创建线程 1,名称是 thread1,入口是 thread1_entry,动态创建*/
rt_thread_t tid1 = rt_thread_create("heartbeat", thread_heartbeat_entry, RT_NULL, 256, 25, 5);
/* 如果获得线程控制块,启动这个线程 */
if (tid1 != RT_NULL)
rt_thread_startup(tid1);
return 0;
}
int main(void)
{
int count = 1;
thread_heartbeat();
DAC_Device_Init();
write_dac_data(2048);
while (count)
{
rt_thread_mdelay(1000);
}
return RT_EOK;
}
五、测试验证
通过在main函数中调用写DAC函数,就可以实现对DAC数据的发送,并且通过测试设置的电压和实际的电压是相吻合的,所以该芯片的驱动程序可用,并且该程序还是使用设备的方式来实现的,后面的RTT设备编程可以参考本次例程。