智能称体脂称实现(代码与基本数据处理篇)

(本文均出于个人理解而写,仅用于学习和交流,某些过程可能不一定正确,希望各位提出意见进行交流,共同进步)

AFE4300的配置是比较简单的,从配置到处理基本的数据,主要有3个方面:SPI配置,AFE4300配置,基本的数据处理。(由于当时板子没做成一块,用STM32产生1M时钟再用杜邦线连接时干扰较大,于是时钟没有用STM32产生)

SPI配置与接口封装

STM32使用库函数来进行开发可以加快开发进度,为了方便,我们也使用了STM32的库函数,按照AFE4300的SPI配置说明,其初始化函数可参考如下:
void SPI2_Init(void)
{
 	GPIO_InitTypeDef GPIO_InitStructure;
  	SPI_InitTypeDef  SPI_InitStructure;

	RCC_APB2PeriphClockCmd(	RCC_APB2Periph_GPIOB, ENABLE );//PORTB时钟使能 
	RCC_APB1PeriphClockCmd(	RCC_APB1Periph_SPI2,  ENABLE );//SPI2时钟使能 	
 
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13 | GPIO_Pin_14 | GPIO_Pin_15;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;  //PB13/14/15复用推挽输出 
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOB, &GPIO_InitStructure);//初始化GPIOB

 	GPIO_SetBits(GPIOB,GPIO_Pin_13|GPIO_Pin_14|GPIO_Pin_15);  //PB13/14/15上拉

	SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex;  //设置SPI单向或者双向的数据模式:SPI设置为双线双向全双工
	SPI_InitStructure.SPI_Mode = SPI_Mode_Master;		//设置SPI工作模式:设置为主SPI
	SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b;		//设置SPI的数据大小:SPI发送接收8位帧结构
	SPI_InitStructure.SPI_CPOL = SPI_CPOL_Low;		//串行同步时钟的空闲状态为低电平,看手册
	SPI_InitStructure.SPI_CPHA = SPI_CPHA_2Edge;	//???串行同步时钟的第二个跳变沿(上升或下降)数据被采样	(两个都可以试试)
	SPI_InitStructure.SPI_NSS = SPI_NSS_Soft;		//NSS信号由硬件(NSS管脚)还是软件(使用SSI位)管理:内部NSS信号有SSI位控制
	SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_256;		//定义波特率预分频的值:波特率预分频值为256
	SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;	//指定数据传输从MSB位还是LSB位开始:数据传输从MSB位开始
	SPI_InitStructure.SPI_CRCPolynomial = 7;	//CRC值计算的多项式
	SPI_Init(SPI2, &SPI_InitStructure);  //根据SPI_InitStruct中指定的参数初始化外设SPIx寄存器
 
	SPI_Cmd(SPI2, ENABLE); //使能SPI外设
	
	SPI2_ReadWriteByte(0xff);//启动传输		 
}   
另外有代码进行其他一些设置,为了方便使用SPI进行读写,在STM32的SPI读写函数的基础上,我们又封装了两个函数,spiWrite和spiRead。
spiWrite函数用于写寄存器,其调用方法非常简单,函数可参考如下:
/**
 *spiWrite - 写AFE4300寄存器
 *@spiAddr:寄存器地址
 *@spiData:16位的数据
 */
void spiWrite (unsigned char spiAddr, unsigned short spiData)
{//先发送地址,再发送数据高8,低8
  
  SPI_AFE4300_CS = 1;
  SPI_AFE4300_CS = 0;		 //使能器件 
  
  SPI2_ReadWriteByte(spiAddr); 

  SPI2_ReadWriteByte(spiData >> 8);//   Load MSB write data

  SPI2_ReadWriteByte(spiData);		 	// Load LSB write data
  
  SPI_AFE4300_CS = 1;
}
spiRead函数用于读寄存器,代码可参考如下:
/**
 *spiRead - 读取AFE4300寄存器
 *@spiAddr:寄存器地址
 *返回unsigned short 类型的数据(16位)
 */
unsigned short spiRead(unsigned char spiAddr)
{
  unsigned short spiData;
  SPI_AFE4300_CS = 1;
  SPI_AFE4300_CS = 0;		 //使能器件 
  
  spiData = SPI2_ReadWriteByte(0x20 | spiAddr);    //发送读取状态寄存器命令,返回读取到的值 现在这个是没用的   
 
  spiData = (SPI2_ReadWriteByte(0x00)) << 8; // Read MSB data
  
  spiData |= SPI2_ReadWriteByte(0x00);// Read LSB data

  SPI_AFE4300_CS = 1;		 //取消片选 
  
  spiWrite (spiAddr, spiData);	 // Writeback read data due to feature bug on the BCM device

  return spiData;			// Return SPI read data
}
有了这两个接口,对于AFE4300的配置就更加的方便和简洁。

AFE4300配置

AFE4300的配置完全按照其datasheet所写进行初始化。其中复位引脚RST(53)低电平复位,高电平正常操作,在复位完成之后,即可开始对AFE4300进行初始化。
以IQ模式为例,其初始化可参考下面的一小段代码:
// Reset AFE4300
	GPIO_ResetBits(GPIOG,GPIO_Pin_14);
	delay_ms(1);
	GPIO_SetBits(GPIOG,GPIO_Pin_14);

	  //0x01 ADC_CONTROL_REGISTER1 这里很多位都是配置不同功能的
	  spiWrite(0x01,0x4170);	//860SPS 
	  //DEVICE_CONTROL1 第0位和第2位 和电源相关
	  //开BMP还是体重
	  spiWrite(0x02,0x0000);//空的寄存器
	  spiWrite(0x03,0xFFFF);  //空的寄存器
	  spiWrite(0x09,0x6006);
	  //设置DAC频率	   250k
	  spiWrite(0x0E,0x00FF);

	 //开一个电流的通道     0:+ 1:-
	  spiWrite(0x0A,0x0408);
	  //开一个电压测量通道	 0:+ 1:-
	  spiWrite(0x0B,0x0408);
//分频    IQ_DEMOD_CLK      BCM_DAC_FREQ
// 1		 1M					250K !
// 2		 500K				125K !
// 4		 250K				62.5K
// 8		 125K				31.25K
// 16		 62.5K				15.625K
// 32		 31.25K				7.8125K
	  //开启IQ模式
	  spiWrite(0x0C,0x0800);
	  //IQ_DEMOD_CLK_DIV_FAC:1分频
	  spiWrite(0x0F,0x0000);


配置好了之后即可进行ADC的读取。

基本的数据处理

我们测量人体的阻抗得到的是一个复阻抗,如下面的公式所述
                                             智能称体脂称实现(代码与基本数据处理篇)_第1张图片
由IQ调制解调的原理,最终可以得到I分量和Q分量如下:
                                              智能称体脂称实现(代码与基本数据处理篇)_第2张图片
进而有
                                                           智能称体脂称实现(代码与基本数据处理篇)_第3张图片
我们测量所得到的是I分量和Q分量的值,因此通过上述公式可以算出阻抗Z,但是还有一个系数K为止,芯片有几个引脚是用来连接校准电阻的,但是由于干扰的情况不同,K并不是唯一的,即K也是一个变量,会随着外界的环境而变。我们可以通过分别开启IQ通道读取IQ分量:
	  spiWrite(0x10,0x0063); //开启I通道
	  afe4300Data_new = spiRead(0);//读ADC
          spiWrite(0x10,0x0065); //开启Q通道
 	  afe4300Data_new = spiRead(0);//读ADC
但是,直接读取ADC的值的时候,数据的波动是非常大的,因此需要采取滤波,至于采取什么滤波算法,因为之前接触到一些传感器的滤波都是使用卡尔曼滤波,出于学习的目的,我也是使用了卡尔曼滤波,卡尔曼滤波的相关学习可参考我的另一篇博客 项目应用中的卡尔曼滤波 。使用了卡尔曼滤波后数据变得比较稳定,但是K值怎么确定呢?我的做法是采用最小二乘法进行数据拟合。我们制作了一排电阻,其阻值由小到大,然后使用AFE4300对这些电阻进行测量,研究其测量值与真实值之间的关系,将结果使用matlab进行处理,代码如下(包含测试数据):
function AFE4300_Data
clc;
clear;
close all;
%a,b,保存实际测得的数据
x = [0.782586286	4.660827093	16.670862	20.89953036	27.18909184	32.8900304	45.89909348	57.44171326	84.29415621	102.9043944];%测量值 频率
y = [19.7 39.5 98.8 119.1 149.1 177.7 239.7 296.0 423.0 512.0];%实际值
hold on
h1 = plot(x,y)%画出实际的曲线
legend([h1(1)], '实际曲线');%显示格式
p = polyfit(x,y,1)%进行线性拟合 p保存的系数从高到低
xlabel('测量值');
ylabel('真实值');

figure
t=0:200;
s = 4.8128*t + 18.0136;
hold on
h2 = plot(t,s,'r+')
s = polyval(p,t);%画出拟合后的曲线
h3 = plot(t,s,'b')
xlabel('测量值');
ylabel('真实值');
legend([h2(1) h3(1)], '算式拟合','拟合曲线');%显示格式
可以看到结果呈现很好的线性,因此可以得到K值,K值算出来后,可用已经得到的K值,带回去测量那一排电阻,得到的结果与真实值很接近,因此这种做法应该是可行的。
按照这种方法可以得到人体的阻抗,当然这只是第一步,由阻抗在推算出其他的东西需要做非常多的工作,TI的工程师说有很多公司有很多的paper在研究这个。具体要自己再去看相关的论文。
关于AFE4300的参考电路和代码均上传到AFE4300参考资料 ,供各位下载。
                                                            智能称体脂称实现(代码与基本数据处理篇)_第4张图片

你可能感兴趣的:(嵌入式项目练习)