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”,并改成十进制,运行仿真
实际拿万用表检测电压值,对应
改变数值,再检测
多改变几次
结果正常