tlv2548与STM32的SPI通信

 2022.7.1更新一下。

第6步读数据,提到的外部时钟好像读不出数据的问题解决了。之前配置外部时钟(CFR数据位D([8,7])),datasheet里面提到说需要14sclk的转化时间(conversion time)。这一点一直没有深入理解。

 最近重新又研究了一遍datasheet,看到了这么一幅图。他包括了一开始4个sclk的指令,中间12or24sclk的采样时间(sampling time),和后面14sclk的转化时间(conversion time)。

 我程序里面用的12sclk的采样时间,所以发送的时候,发送了半字(HalfWord),但注意到,这里的转化时间,也是需要时钟的。而程序中,我发送完半字后,不再发内容的话,时钟就没有了(这个可以用示波器测到)。因此,需要在发送半字内容后,再发送半字,保证有时钟产生(我这里就是发送了通道选择指令后,再发上16bits的0)。

	for(int i = 0; i < NumOfFIFO; i++)
	{
		/* 开始通讯:CS低电平*/
		ADC_SPI_CS_LOW();
				
		ADC_SPI_SendHalfWord(WriteBuf[i]);
		
		ADC_SPI_SendHalfWord(0x0000);
		
		//SysTick_Delay_Us(10);
		
		/* 结束通讯:CS高电平*/
		ADC_SPI_CS_HIGH();
	}
}

这样,就可以用外部时钟进行转化了。

——————————————————————————————————————————

2022.6.27第一次更新

最近的课题有用到stm32f103利用SPI通信去读ADC的数据,用的ADC是tlv2548。stm32的学习是用的野火的开发板,也是刚入门的水平,调这个spi程序搞了好久,而且好像也没搜到这个adc的编程资料,备受煎熬。现在终于是能读出数据了,发个帖子记录分享一下,欢迎用同样芯片的同学跟我讨论,共同进步。

0.先贴一下原理图。需要注意一下FS好像是跟DSP用的,这里没用就直接接了电源,CSTART好像可以自己设置采样时间,是高级用法,这里没有用,虽然没有直接接电源,但用stm32将这个引脚设为了高电平,也跟接电源一样了。

1.tlv2548的SPI通信,需要配置成固定的模式,即CPOL=0, CPHA=0。关于速度的话,他说不超过20MHz,我没有仔细研究别的行不行,我这里用的32分频。

SPI_InitStructure.SPI_CPOL = SPI_CPOL_Low;
SPI_InitStructure.SPI_CPHA = SPI_CPHA_1Edge;
SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_32;

2.tlv2548我这里配置的是一次发送半字,16位。一次发送8位一个字节也可以。

SPI_InitStructure.SPI_DataSize = SPI_DataSize_16b;

3.读写的函数套用的野火的模板,他这里一个函数同时实现了发送和读取

/**
  * @brief  使用SPI发送两个字节的数据
  * @param  byte:要发送的数据
  * @retval 返回接收到的数据
  */
u16 ADC_SPI_SendHalfWord(u16 HalfWord)
{
	  SPITimeout = SPIT_FLAG_TIMEOUT;
  /* 等待发送缓冲区为空,TXE事件 */
  while (SPI_I2S_GetFlagStatus(ADC_SPIx , SPI_I2S_FLAG_TXE) == RESET)
	{
    if((SPITimeout--) == 0) return SPI_TIMEOUT_UserCallback(2);
   }
	
  /* 写入数据寄存器,把要写入的数据写入发送缓冲区 */
  SPI_I2S_SendData(ADC_SPIx , HalfWord);

	 SPITimeout = SPIT_FLAG_TIMEOUT;
  /* 等待接收缓冲区非空,RXNE事件 */
  while (SPI_I2S_GetFlagStatus(ADC_SPIx , SPI_I2S_FLAG_RXNE) == RESET)
	 {
    if((SPITimeout--) == 0) return SPI_TIMEOUT_UserCallback(3);
   }
  /* 读取数据寄存器,获取接收缓冲区数据 */
  return SPI_I2S_ReceiveData(ADC_SPIx );
}

4.tlv2548说是要在使用的时候发送0xA000初始化确定型号.

void ADC_init()
{
	uint16_t CFR_Writed = 0;

	CFR_Writed = 0xA000;

	WriteCFRdata(CFR_Writed);
	
}

5.tlv2548好像是只有1个16位寄存器CFR(Configuration Register)(所以我上面SPI的数据位也设成16位,方便操作),CFR的写入和读取操作最简单,所以我是从这步操作开始入门的。但需要注意的是CFR的读取只有在5,6位数据为00时才能使用

这里写入了0xA102(第一位A是写入的意思),试一试能不能读出来 ,先拉低CS,再发送0xA102,再拉高CS。

void CFR_Write()
{

  ADC_SPI_CS_LOW();
	
  ADC_SPI_SendHalfWord(0xA102);

  ADC_SPI_CS_HIGH();

}

贴一张CFR写入的时序图

发送0x9000读取CFR(第一位9是读取的意思,后面三位随意,这里就设为0),拉低CS,发送0x9000并读取数据,CS拉高

u16 CFR_Read()
{
  u16 Temp = 0;

  ADC_SPI_CS_LOW();

  Temp = ADC_SPI_SendHalfWord(0x9000);

  ADC_SPI_CS_HIGH();

  return Temp;
}

贴一张CFR读取的时序图

6.读一下数据

这里读数据用的是mode11,因为时序图最简单hhh

首先进行上图第1步模式配置,这里是内部参考,4V,12sclk采样,内部时钟转换(这里的内部时钟转换需要注意,我开始是设的另外3中外部时钟的模式,但好像读不出来数据,不知道为什么。然后偶然间改成了内部时钟转换,才能读出来数据了),重复扫描模式,启用中断,8个FIFO

void ADC_Mode10_Config(uint16_t NumOfFIFO)
{
	uint16_t CFR_Writed;
	
	CFR_Writed = REF_INTER|INT_REF_4V|SHORT_SAM_12_SCLK|CON_CLK_OSC|RER_SWE_MOD|SWE_SEQ_S0|INT|FIFO_FULL; 
			
	WriteCFRdata(CFR_Writed);
	
}

然后第2步,设置通道0x0000~0x7000。这里设了个10us的延时,因为看到说内部时钟转换的话有3.86us转换时间

void SelectChannel(uint16_t NumOfFIFO)
{
	
	uint16_t WriteBuf[8]={0, 0, 0, 0, 0, 0, 0, 0}; 
	
	WriteBuf[0] = CHANNEL0;	 // 0x0000
	WriteBuf[1] = CHANNEL1;	 // 0x1000
	WriteBuf[2] = CHANNEL2;	 // 0x2000
	WriteBuf[3] = CHANNEL3;	 // 0x3000
	WriteBuf[4] = CHANNEL4;	 // 0x4000
	WriteBuf[5] = CHANNEL5;	 // 0x5000
	WriteBuf[6] = CHANNEL6;	 // 0x6000
	WriteBuf[7] = CHANNEL7;	 // 0x7000
	
	for(int i = 0; i < NumOfFIFO; i++)
	{
		
		ADC_SPI_CS_LOW();
				
		ADC_SPI_SendHalfWord(WriteBuf[i]);
		
		SysTick_Delay_Us(10);
		
		ADC_SPI_CS_HIGH();
	}
}

然后第3步,发送0xE000读数据

void ReadFIFOdata(uint16_t *DataBuffer, uint16_t NumOfFIFO)
{
	
	uint16_t WriteBuf[1]={0}; 
	
	WriteBuf[0] = R_FIFO;//0xE000	 
	
	for(int i = 0; i < NumOfFIFO; i++)
	{			
		
		ADC_SPI_CS_LOW();	
		
		DataBuffer[i] = ADC_SPI_SendHalfWord(WriteBuf[0]);
					
		ADC_SPI_CS_HIGH();
	}
	
}

7. 配置中断,INT是从高变到低,所以配置了下降沿中断

EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling;

和相应的中断服务函数,设个flag判断有没有进中断

void KEY1_IRQHandler(void)
{
  //确保是否产生了EXTI Line中断
	if(EXTI_GetITStatus(KEY1_INT_EXTI_LINE) != RESET) 
	{
		flag = 1;
    //清除中断标志位
		EXTI_ClearITPendingBit(KEY1_INT_EXTI_LINE);     
	}  
}

8.主函数main中的一些处理。首先是CFR写入和读取

ADC_init();
CFR_Write();
CFR = CFR_Read();
printf("\r\n CFR is 0x%X\r\n", CFR);

然后是读ADC数据,先进行CFR配置(对应步骤1),while循环,选择通道(对应步骤2)。这时候按说已经产生了中断,flag=1。判断是否进中断,进中断了读FIFO数据(对应步骤3),FIFO是前12位有效,所以做一下处理。再将flag置0,然后再重新选择通道,(对应步骤2的循环),每过1s打印一次数据。


	
ADC_Mode10_Config(NumOfFIFO);
		
while(1)
{	
	SelectChannel(NumOfFIFO);
	
	if(flag == 1)
	{
			
		ReadFIFOdata(DataBuffer, NumOfFIFO);
			
		for(int i = 0; i < NumOfFIFO; i++)
		{
			adc[i] = (DataBuffer[i] >> 4) * 3.3 / 4095;
			printf("ADC = %04f V\n", adc[i]);
			adc[i] = 0.0;
		}
				
		flag = 0;

	}

    SysTick_Delay_Ms(1000);
}

9. 目前大概理解了这些,有什么问题大家可以讨论,共同进步。

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 6
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值