STM32H5 SPI接口 访问非标准SPI ADC(AD7768)

参考:Manipulating MCU SPI Interface to Access a Nonstandard SPI ADC 

目录

简介 

标准SPI连接ADC芯片

AD7768 采用STM32微控制器 SPI 通过一条 DOUT 线读取代码

解决方案 1:MCU SPI 作为节点通过一条 DOUT 线与 SPI 主 ADC 连接

​解决方案 2:MCU SPI 作为节点通过两条 DOUT 线与 SPI 主 ADC 连接 


简介 

当前许多的精密模数转换器(ADC)模块都有串行外设接口(SPI)或一些串行接口,用于控制器通信。而为了使ADC实现更快的吞吐速率,一些ADC采用了具有非标准三线或四线SPI作为节点。

本次使用的AD7768芯片具有多条SDO线路,它们用作SPI电源。在使用该类ADC芯片时需要使用非标准SPI读取数据。

图1.AD7768具有两个数据输出引脚。

标准SPI连接ADC芯片

SPI 是一个同步的、全双工的、基于主/节点的接口。来自 main 或 node 的数据在 rising 或 falling clock edge 上同步。main 和 node 都可以同时传输数据。图 2 显示了典型的 4 线 MCU SPI 接口连接。

图2.标准SPI连接ADC芯片

如果MCU的SPI和ADC串行接口具有标准的SPI时序模式,可使用MCU标准SPI读取ADC数据,须遵循SPI协议规范。但是有一些新的ADC具有非标准SPI时序行接口端口。MCU无法使用标准SPI读取ADC7768串行端口数据。

 图3.AD7768 FORMATx = 1× 时序图输出仅在DOUT0上

表1. FORMATx真值表 - AD7768

如表1 所示,当 FORMATx = 11 或 10 时,通道 0 到通道 7 仅在 DOUT0 上输出数据。在标准模式下,AD7768/AD7768-4 作为主电源工作,并将数据流传输到 MCU、DSP 或 FPGA。AD7768/AD7768-4 为节点器件提供数据、数据时钟 (DCLK) 和下降沿成帧信号(DRDY)。

本文将介绍操作标准微控制器 SPI 以与具有非标准 SPI 端口的 ADC(AD7768) 连接的方法。 

AD7768 采用STM32微控制器 SPI 通过一条 DOUT 线读取代码

STM32 微控制器广泛用于许多不同的应用。MCU 具有多个 SPI 端口,可以配置为具有典型 SPI 时序模式的 SPI 主端口或节点端口。以下会话中介绍的方法也可以应用于具有 8 位、16 位或 32 位帧的其他微控制器。

AD7768 的串行接口不是典型的 SPI 时序模式,AD7768 用作串行接口主接口。通常,用户必须使用 FPGA/CPLD 作为其控制器。

例如,使用 STM32 和 AD7768 评估板。解决方法 SPI 线的连接如图 4 所示。在此设置中,所有八个 AD7768 通道数据输出仅在 DOUT0 上。

图4.MCU SPI连接

需要解决的问题:

  • AD7768 用作 SPI 主器件,因此 STM32F429I SPI 必须配置为 SPI 节点。
  • DRDY 高脉冲只是 DCLK 持续时间的一个周期,不是典型的 CS。
  • DCLK 连续输出,当所有通道数据位输出完成时,DRDY 为低电平。

图5.AD7768 数据读入时序解决图解 

解决方案 1:MCU SPI 作为节点通过一条 DOUT 线与 SPI 主 ADC 连接

  • 将MUC任意一SPI端口配置为节点,以在MOSI上接收AD7768主节点(DCLK)时钟。
  • 将AD7768 DRDY连接到MCU外部中断输入引脚和NSS(SPI CS)引脚上。AD7768 DRDY下升沿数据就绪。为了使MCU有足够的响应时间,所以设置MCU外部中断上升沿触发。
  • 在收到从通道 0 到通道 7 的所有数据后,应禁用 SPI 以防止读取额外的无效数据,因为 DRDY 使 SPI 节点 CS 为低电平,而 DCLK 不断切换。

这里我使用的芯片是STM32H563VGTx 使用CubeMX初始化了SPI2作为节点,读取AD7768的数据位。外部中断1判断数据是否准备就绪。

 SPI2初始化代码:

  //SPI寄存器基地址  
  hspi2.Instance = SPI2;       
  //指定工作模式为从机                 
  hspi2.Init.Mode = SPI_MODE_SLAVE;            
  //双向工作状态
  hspi2.Init.Direction = SPI_DIRECTION_2LINES_RXONLY;   
  //指定SPI数据大小
  hspi2.Init.DataSize = SPI_DATASIZE_32BIT;      
  //指定串行时钟的稳定状态
  hspi2.Init.CLKPolarity = SPI_POLARITY_HIGH;    
  //指定串行时钟的采样状态
  hspi2.Init.CLKPhase = SPI_PHASE_1EDGE;  
  //指定NSS信号是硬件管理还是软件管理       
  hspi2.Init.NSS = SPI_NSS_SOFT;       
  //指定串行数据是高位开始还是低位开始     
  hspi2.Init.FirstBit = SPI_FIRSTBIT_MSB;
  //指定是否使用TI模式    
  hspi2.Init.TIMode = SPI_TIMODE_DISABLE;    
  //指定是否启用CRC计算
  hspi2.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE; 
  //指定CRC计算的多项式
  hspi2.Init.CRCPolynomial = 0x7;    
  //指定是否启用NSSP信号
  hspi2.Init.NSSPMode = SPI_NSS_PULSE_DISABLE;   
  //指定NSS信号 哪个电平为活动电平 
  hspi2.Init.NSSPolarity = SPI_NSS_POLARITY_LOW;    
  //FIFO阈值级别
  hspi2.Init.FifoThreshold = SPI_FIFO_THRESHOLD_01DATA;    
  //指定一个额外的延迟 以SPI时钟周期的数量表示 在主动边缘和第一个数据事务以主模式启动之间额外插入
  hspi2.Init.MasterSSIdleness = SPI_MASTER_SS_IDLENESS_00CYCLE;    
  //指定在主模式下插入两个连续数据帧之间的最小时间延迟(以SPI时钟周期表示)。
  hspi2.Init.MasterInterDataIdleness = SPI_MASTER_INTERDATA_IDLENESS_00CYCLE;
  //控制SPI在主接收模式下的连续传输,并进行自动管理,避免出现超限情况。
  hspi2.Init.MasterReceiverAutoSusp = SPI_MASTER_RX_AUTOSUSP_DISABLE;
  //备用功能GPIO状态控制
  hspi2.Init.MasterKeepIOState = SPI_MASTER_KEEP_IO_STATE_DISABLE;
  //反转MOSI/MISO
  hspi2.Init.IOSwap = SPI_IO_SWAP_DISABLE;
  //指定RDY信号 是否在内部管理
  hspi2.Init.ReadyMasterManagement = SPI_RDY_MASTER_MANAGEMENT_INTERNALLY;
  //指定RDY信号输入引脚 哪个电平为活动电平
  hspi2.Init.ReadyPolarity = SPI_RDY_POLARITY_HIGH;

使能外部中断1的中断服务函数代码(为了使MCU有足够的响应时间  将中断设置为上升沿触发): 略

中断服务函数:

void HAL_GPIO_EXTI_Rising_Callback(uint16_t GPIO_Pin)
{
	if(GPIO_Pin == GPIO_PIN_1)	   //判断是否为外部中断1触发
	{
        //中断触发表示数据就绪(DRDY上升沿) 开启SPI2 接收数据
		__HAL_SPI_ENABLE(&hspi2);    
        //清除中断标志位
		__HAL_GPIO_EXTI_CLEAR_IT(GPIO_PIN_4);
        //主程序中的标志位    
		ad7768_flag = 1;
	}
}

读取AD7768数据位主循环代码:

if(ad7768_flag == 1)    //等待中断标志置1
{
	ad7768_flag = 0;    //清除中断标志
    //抛出前一个ODR周期中 捕获的最后一个字节
	Rx_temp = *(__IO uint8_t*)&hspi2.Instance->RXDR;    
	while(SPI2_ByteCount < SPI2_CNVByteNum)    //读取SPI2_CNVByteNum通道数据
	{
		if(__HAL_SPI_GET_FLAG(&hspi2,SPI_FLAG_RXWNE)) //等待接收缓冲区非空
		{
            //将接收到的数据 从数据寄存器写入内存
			SPI_RxBuffer[SPI2_ByteCount] = *(__IO uint8_t*)&hspi2.Instance->RXDR;
			SPI2_ByteCount++;    //接收数据自增
		}
	}
    //每接收一组数据后(DRDY一个下降沿) 将SPI2关闭 防止读取额外的无效数据
	__HAL_SPI_DISABLE(&hspi2);    
	SPI2_ByteCount = 0;    //将计数变量清零
}

当软件处于中断模式时,DCLK 可以运行高达 4 MHz,并实现 ODR 8 kSPS。软件应进入中断处理程序,在一个半 DCLK 周期时间 (375 ns) 内启动 SPI。为了更轻松地使软件进入中断例程,MCU 可以在 DCLK 上升沿读取数据,这可以提供额外的半个 DCLK 周期时间。但是,由于 t5DCLK 上升到 DOUTx 无效最小值为 –3 ns(IOVDD = 1.8 V 时为 –4 ns),传播延迟 (>|t5|+ MCU 保持时间)应由 PCB 布线或缓冲器添加。

​解决方案 2:MCU SPI 作为节点通过两条 DOUT 线与 SPI 主 ADC 连接 

在第一种解决方案中,仅使用 DOUT0 输出所有 8 通道数据。因此,数据读取将 ADC 吞吐速率限制为 8 kSPS。如图 1 所示,DOUT0 上的通道 0 到通道 3 输出和 DOUT1 上的通道 4 到通道 7 输出可以减少数据传输时间。通过这样的改进,ODR 在 DCLK 4 MHz 时可以轻松达到 16 kSPS。
​编辑


固件可以使用轮询模式而不是中断模式来减少 DRDY 上升沿触发器的时间延迟,使 SPI 能够接收数据。这可以在 DCLK 8 MHz 时实现 ODR 32 kSPS。 

//等待产生中断 (中断方式 采用中断服务函数置标志位的方式 而轮询方式采用轮询查看是否产生中断)
while(__HAL_GPIO_EXTI_GET_IT(GPIO_PIN_4) != SET);
		
//检测到中断产生后 开启SPI读取数据	
__HAL_SPI_ENABLE(&hspi3);
__HAL_SPI_ENABLE(&hspi2);

//清除中断标志		
__HAL_GPIO_EXTI_CLEAR_IT(GPIO_PIN_4);
//采集双线八通道数据 所以需要采集4轮
while(SPI2_ByteCount < 4)		
{
    //等待接收缓冲区非空
	if(__HAL_SPI_GET_FLAG(&hspi3,SPI_FLAG_RXWNE))
	{
        //读取数据寄存器
		SPI_RxBuffer[SPI2_ByteCount][SPI2_j] = *(__IO uint32_t*)&hspi2.Instance->RXDR;
		SPI_RxBuffer[4+SPI2_ByteCount][SPI2_j] = *(__IO uint32_t*)&hspi3.Instance->RXDR;
		SPI2_ByteCount++;
	}
}
//关闭SPI
__HAL_SPI_DISABLE(&hspi2);
__HAL_SPI_DISABLE(&hspi3);
SPI2_ByteCount = 0;
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值