(本文均出于个人理解而写,仅用于学习和交流,某些过程可能不一定正确,希望各位提出意见进行交流,共同进步)
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的读取。
基本的数据处理
我们测量人体的阻抗得到的是一个复阻抗,如下面的公式所述由IQ调制解调的原理,最终可以得到I分量和Q分量如下:
进而有
我们测量所得到的是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在研究这个。具体要自己再去看相关的论文。