STM32F103Cx用芯片tlc5615做DAC转换

1、打开原理图,查看芯片对应的相应管脚

 

 2、打开STM32CubeMX,添加相应管脚配置

3、输入的配置为无上下拉,输出该芯片对应的引脚配置和上图相同,一键生成代码,打开代码

4、新建“BspVout.c”和“BspVout.h”文件并添加在结构中,在“AppTask.h”文件中添加头文件“  #include "BspVout.h"   ”

5、在“BspVout.h”文件中添加代码

#define SPI_CS(x)  (HAL_GPIO_WritePin(GPIOA, GPIO_PIN_15, x))
#define SPI_CLK(x) (HAL_GPIO_WritePin(GPIOA, GPIO_PIN_3, x))
#define SPI_DIN(x) (HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, x))

作用是用宏定义控制输出信号,方便模拟SPI信号

6、分析数据手册

 这是一张时序图,说明当CS信号为低电平的时候才能通讯,SCLK上升沿的那一瞬间读取DIN的电平信号

当CS是高电平时,SCLK为低电平,当CS由高电平转为低电平信号,SCLK的第一个上升沿读取的DIN电平信号为第一位数字信号,下图有说明

 这里选择的是非菊花链式,所以按照Figuer 10 所示,数据信息与输出的关系见下图 

 这是一个十二位的锁存器,所以要十二的数字量,最后两位无效,但是必须加上

电压的输出关系是 Uout=2*Vrefin*输入的十二位(高十位有效)数值/1024,其中Vrefin是外部输入给芯片的参考电压,值为+2.5V,这个值要精准一些。

7、“BspVout.c”的代码如下

#include "BspVout.h"

unsigned short Vount;

void SPI_Vout(unsigned short Vout)
{
	unsigned short VData;
	VData = (unsigned short)(Vout * (1.024 / 2 / 2.5)+0.5);//由目标电压mV,计算十位数值
	SPI_CS(H);
	SPI_CLK(L);
	SPI_DIN(L);
	for(unsigned short i;i<3;i++);//>1ns  th(CSH0)
	SPI_CS(L);
//	for(unsigned char j=0;j<4;j++)//前四位
//	{
//		SPI_DIN(L);
//		for(unsigned short i;i<45;i++);//上升沿读取数值 >45ns  tsu(CSS)、tsu(DS)、tw(CL)
//		SPI_CLK(H);
//		for(unsigned short i;i<45;i++);//>25ns tw(CH)、th(DH)
//		SPI_CLK(L);
//	}
	for(unsigned char j=0;j<10;j++)//十位数据 ,j代表第几位(含0位)
	{
		
		if(((VData >> (9-j)) & 0x01) == 0) //计算每一个位的电平
		{
			SPI_DIN(L);
		}
		else
		{
			SPI_DIN(H);
		}
		for(unsigned short i;i<5;i++);//上升沿读取位电平 >45ns  tsu(CSS)、tsu(DS)、tw(CL) 
		SPI_CLK(H);
		for(unsigned short i;i<5;i++);//>25ns tw(CH)、th(DH)
		SPI_CLK(L);
	}
	for(unsigned char j=0;j<2;j++)//补充 后两位
	{
		SPI_DIN(L);
		for(unsigned short i;i<5;i++);//上升沿读取数值 >45ns  tsu(CSS)、tsu(DS)、tw(CL)
		SPI_CLK(H);
		for(unsigned short i;i<5;i++);//>25ns tw(CH)、th(DH)
		SPI_CLK(L);
	}
	
	SPI_DIN(L);
	for(unsigned short i;i<3;i++);//>0ns  th(CSH1)
	SPI_CS(H);
	for(unsigned short i;i<3;i++);//>20ns  tw(CS)
}



void SYS_test1ms(void)
{
	SPI_Vout(Vount);

}

8、“BspVout.h”的代码如下

#ifndef _BSPVOUT_H_
#define _BSPVOUT_H_

#include "AppTask.h"


#define SPI_CS(x)  (HAL_GPIO_WritePin(GPIOA, GPIO_PIN_15, x))
#define SPI_CLK(x) (HAL_GPIO_WritePin(GPIOB, GPIO_PIN_3, x))
#define SPI_DIN(x) (HAL_GPIO_WritePin(GPIOB, GPIO_PIN_4, x))
#define H           GPIO_PIN_SET  
#define L           GPIO_PIN_RESET  


void SPI_Vout(unsigned short Vout);
void SYS_test1ms(void);

#endif

9、“AppTask.c”的代码

#include "AppTask.h"

extern unsigned short Vount;

vinTime VRtime;

//任务注册表
static TaskCtrl_t	TaskCtrl[] = //执行优先级程度 从上到下依次逐渐,第一个优先级最高,所有的任务函数都不能打断其它的任务函数
{
	/*	【实时任务】	【上电立即运行】	【初始counter】			【任务周期ms】				【任务指针】*/  //时间调度最好是cycle_task的整数倍
	{   1,                  0,              0,                      100/cycle_task,           	taskTestTime},
	{	0,  				0, 				0/cycle_task,			500/cycle_task,				taskTest},
	{	0,					0,				0/cycle_task,			50/cycle_task,				SigLED},
	{	0,					0,				0/cycle_task,			1/cycle_task,				SYS_test1ms},
};

#define TASK_MAX_NUM	(sizeof(TaskCtrl)/sizeof(TaskCtrl[0])) //自动生成任务调度的数量给 TASK_MAX_NUM
	
void App_task(void)
{
	volatile unsigned char i_task, j_task;
	static unsigned int timen_task;
	if(timen - timen_task >= cycle_task)//每间隔 cycle_task ms 执行一次
	{
		timen_task = timen;//和上面if括号的那句共同构成 每隔cycle_task个定时周期更新一次任务等待时长
		for(i_task = 0; i_task < TASK_MAX_NUM; i_task++)//把所有的任务检测一边
		{
			if((TaskCtrl[i_task].Task_Counter > 0) && (TaskCtrl[i_task].Task_RealTime == 0))//用于防止初始计数值为零,自减溢出
				TaskCtrl[i_task].Task_Counter--;
			if((TaskCtrl[i_task].Task_Counter == 0) && (TaskCtrl[i_task].Task_RealTime == 0))//自减为零的时候,认为该任务可以执行
			{
				TaskCtrl[i_task].Task_Running = true;//认为为可执行任务
				TaskCtrl[i_task].Task_Counter = TaskCtrl[i_task].Task_PeriodTime;//更新计时周期
			}
		}
	}
	for(j_task = 0; j_task < TASK_MAX_NUM; j_task++)//排队执行可执行函数
	{// 任务调用
		if(TaskCtrl[j_task].Task_Running || TaskCtrl[j_task].Task_RealTime)
		{
			TaskCtrl[j_task].TaskHook();//执行函数
			TaskCtrl[j_task].Task_Running = false;//执行结束
		}
	}
	
}

sigLEDk sigLED;

void AppInit(void)
{
	// 1-常亮; 2-T=200ms闪烁; 3-T=500ms闪烁; 4-T=1s闪烁; 0-常灭
	sigLED.LED01=1;
	sigLED.LED02=2;
	sigLED.LED03=3;
	sigLED.LED04=4;
	Vount = 1250; //用于Debug,用后删除
}

void taskTest(void)
{
	static unsigned int time;
	for(unsigned char i=0;i<10;i++)
		TxBuff[i]=0x00+i;
	TxBuff[10]=0x0D;
	TxBuff[11]=0x0A;
	if((TxEndFlag == 0)&(timen - time >= 1000))
	{
		time = timen;
		PMJD_UART1_DMA_Send(TxBuff,12);
	}
}

//实时性任务
void taskTestTime(void)
{
	unsigned char TxBuffTest[BUFFER_SIZE];
	if(RxEndFlag == 1)  //接收完成标志
	{
		for(unsigned char i=0;i<RxLen;i++)
		{
			TxBuffTest[i]=RxBuff[i];
		}
		PMJD_UART1_DMA_Send(TxBuffTest, RxLen);//发送接收到的数据
		memset(RxBuff,0,RxLen);//清除接收到的数据
		RxLen = 0;//清除计数
		RxEndFlag = 0;//清除接收结束标志位
	}
}

10、编译下载程序,进入硬件在环仿真

11、在“Watch1”中添加变量 “Vount”,并改成十进制,运行仿真

实际拿万用表检测电压值,对应

 改变数值,再检测

 多改变几次

结果正常

 

  • 6
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
TLC5615 10位高速串行DA模块/正弦波发生器 最新升级V3.0,自带负压发生器,单电源供电即可输出双极性正弦波! 一、模块主要特点 1.10位高速串行DATLC5615,仅占用3个IO口。 2.1%%精度2.048VLM4040电压基准,精确输出电压。 3.直接电压输出,无需运放转换,性能远超DAC0832等电流型DA。 4.输出信号带运放跟随器缓冲输出,阻抗低、线性好! 5.自带运放信号变换,将DA输出减去2.048V,支持直接输出双极性正负脉冲(V2.0需要负电源,V3.0单5V即可)。 6.自带负压发生器(仅V3.0,2.0老版本需要负电源),单5V供电即可输出双极性正弦波。 二、管脚说明 V2.0老版本管脚说明: Vin+:正电源输入,单电源模式输入范围为:5-16V,双电源模式下为3V-8V Vin-:负电源输入,单电源模式下悬空不接,电源选择拨到S模式。双电源输入范围(-3V)-(-8V)。要输出双极性正弦波必须输入负电压,并且将电源选择开关拨到D,即双电源模式。 Aout+:运放跟随器缓冲输出,和DA输出完全相同,阻抗更低。 Aout-:双极性输出,仅双电源模式下可用。将输出信号减去2.048V。硬件支持直接输出双极性正弦波(-2.048V)-(2.048V)。DI:数据输入 SCK:时钟输入 CS:器件片选 Dout:级联输出管脚 V3.0新版本管脚说明: VIN:单5V电源正极输入。 GND:单5V电源负极输入。 Aout+:运放跟随器缓冲输出,0-4.096V。 Aout-:双极性输出端,无需双电源,模块自带负电压发生器,范围(-2.048V)-(+2.048V),Aout- =(2.048V-DA),(即DA输出大于2.048时输出负,小于2.048时输出正。)。 DI:数据输入 SCK:时钟输入 CS:器件片选 Dout:级联输出管脚 确认收货后向客服索要资料,包含电路原理图(仅V3.0,V2.0版本淘汰中)、正弦波、三角波C51程序、相关器件资料等。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

逐梦之程

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值