STM32-SPI3控制MCP3201、MCP3202(Sigma-Delta-ADC芯片)
- 原理图
- 手册说明
- 功能方框图
- 引脚功能
- 数字输出编码与实值的转换
- 分辨率设置与LSB
- 最小和最大输出代码(注)
- 正负符号寄存器位MSB
- 数字输出编码
- 数据转换的LSB值
- 将设备输出编码转换为输入信号电压计算
- 片内寄存器
- 配置寄存器
- 采样与转换过程
- 应用实例
- 电压测量
- 电流测量
- 代码部分
原理图
手册说明
MCP3421是一个单通道低噪声、高精度的ΔΣA/D转换器,具有差分输入和高达18位的分辨率,在一个小型的SOT-23-6包中。机载精度2.048V参考电压使输入范围为±2.048V的差异(Δ电压= 4.096V)。该设备使用一个双线I2C兼容的串行接口,并操作从一个2.7V到5.5V的电源。
MCP3421设备以每秒3.75、15、60或240个样本(SPS)的速率执行转换,这取决于使用双线I2C串行接口的用户可控配置位设置。该装置具有一个机载可编程增益放大器(PGA)。用户可以在进行模数转换之前选择x1、x2、x4或x8的PGA增益。这允许MCP3421设备以高分辨率转换较小的输入信号。该设备有两种转换模式: (a)连续模式和(b)单次模式。在单次模式模式下,设备在单次转换后自动进入低电流待机模式。这大大减少了在空闲期间的电流消耗。
功能方框图
引脚功能
数字输出编码与实值的转换
分辨率设置与LSB
内部参考电压为2.048V
最小和最大输出代码(注)
最大n位编码 =
最小n位编码 =
正负符号寄存器位MSB
当MSB是逻辑“0”时,输入为正。当MSB是一个逻辑“1”时,输入是负的。
注:
1.MSB是一个符号寄存器位: 0:正输入(VIN+>VIN-)1:负输入(VIN+<VIN-)。
2.输出数据格式是二进制二的补充。
数字输出编码
ADC芯片输出的编码二进制数据,翻转计算可算出Vin
数据转换的LSB值
用户可编程位分辨率:12、14、16或18
将设备输出编码转换为输入信号电压计算
如果符号指示位(MSB)为“0”,则输入电压通过将输出码与LSB乘以,并除以PGA设置得到输入电压。如果符号指示器位(MSB)是“1”,则输出代码需要转换为2的补码,然后再乘以LSB并除以PGA设置。表4-4显示了将设备输出代码转换为输入电压的示例。
公式需结合数字输出编码和数据转换的LSB值进行验算
片内寄存器
配置寄存器
开机后的默认配置
文字说明
这一位是数据准备就绪的标志。在读取模式下,此位表示输出寄存器是否已用最新的转换结果更新。在一次性转换模式下,将此位写入“1”将启动一个新的转换
如果在读取数据字节后(即在18位转换模式下的第5个字节之后)通过连续时钟重复读取配置字节,则RDY位的状态指示设备是否准备好了新的转换结果。当主服务器发现RDY位被清除时,它可以发送一个不承认(NAK)位和一个停止位来退出当前的读操作,并为最新的转换数据发送一个新的读命令。一旦读取了转换数据,准备位将切换到“1”,直到下一个新的转换数据准备就绪。每次完成新的转换时,输出寄存器中的转换数据将被覆盖。图5-3和图5-4显示了读取转换数据的示例。用户可以随时为一个新的设置重写配置字节。表5-1和表5-2给出了配置位操作的示例。
采样与转换过程
也就是说采样时间为1.5倍的CLK周期。在采样的过程中,器件内部的采样保持电容会收集输入通道的电荷,采样的模型图如下:
如上图所示,信号源阻抗Rss与MCP3201内部采样开关的阻抗Rs将直接影响给电容CSAMPLE充电所需的时间。因此,较大的信号源阻抗会增加转换的失调误差、增益误差和积分线性误差。
当采样结束后,打开转换器的输入开关,MCP3202将开始把内部采样保持电容收集的电荷产生一个12位的串行数字输出编码。MCP3202每收到一个时钟脉冲,就转换一位,共收到12个脉冲,刚好输出一个12位的输出编码值。
值得注意的是,如果时钟速率太慢,采样电容将在转换过程中释放电荷。在85度(最差条件)下,器件能保持采样电容在采样周期结束后至少1.2ms内不会释放电荷。也就是说,从采样周期结束到所有12个数据位都输出之间的时间不能超出1.2ms,即时钟频率要大于10KHz。若此条件得不到满足,就会导致线性误差超出额定规范值。
在整个转换周期内,只要满足时序上的时间最小值要求,对于时钟是否恒定和占空比并没有要求。
应用实例
电压测量
测量电池电压的电路如下图所示
对于微弱电压,可以使用内部可编程电路放大增益放大器(PGA)进行放大处理,增益高达到8。
测量的模拟输入电压,计算公式
电流测量
测量电流的电路如下图所示
测量电流,计算公式
代码部分
以STM32F103和标准库作为底板
main.c
#include "led.h"
#include "delay.h"
#include "key.h"
#include "sys.h"
#include "usart.h"
#include "bsp_spi.h"
/************************************************
ALIENTEK精英STM32开发板实验4
串口 实验
技术支持:www.openedv.com
淘宝店铺:http://eboard.taobao.com
关注微信公众平台微信号:"正点原子",免费获取STM32资料。
广州市星翼电子科技有限公司
作者:正点原子 @ALIENTEK
************************************************/
int main(void)
{
delay_init(); //延时函数初始化
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //设置NVIC中断分组2:2位抢占优先级,2位响应优先级
uart_init(115200); //串口初始化为115200
Spi3_init();
/*
实测HG-C1400激光传感器
最大正数值204.0mm 对应电压为5.03V
最大负数值-203.6mm 对应电压为0.003V
总测试距离 204+203.6=407.6
中间值为 407.6/2=203.8
每0.1mm对应电压为 407.6/(4.997*0.012259) = 0.01226V
分压电阻 0.01226/2=0.006129
*/
while(1)
{
printf("%f\n",(((float)Get_Adc_Average(8,255)*4.072/8191)/(0.01226))*1.9721-200.4); //1.9721 为 分压 200.4 为中间距离 以上通过线性回归
delay_ms(250);
}
}
bsp_spi.c
#include "bsp_spi.h"
#include "led.h"
#include "delay.h"
#include "key.h"
#include "sys.h"
#include "usart.h"
/**
* 函数功能: SPI3_初始化
* 输入参数:
* 返 回 值:
* 说 明:无
*/
void Spi3_init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
SPI_InitTypeDef SPI_InitStructure;
/* 使能GPIO和SPI时钟 */
RCC_APB2PeriphClockCmd( SPI_SCK_CLK , ENABLE );//PORTB时钟使能
RCC_APB1PeriphClockCmd( SPI_CLK , ENABLE );//SPI2时钟使能
GPIO_PinRemapConfig(GPIO_Remap_SWJ_JTAGDisable,ENABLE) ;
/*将PA15弄成普通IO */
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_15;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //推挽输出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
GPIO_SetBits(GPIOA,GPIO_Pin_15);//使能器件
/* 配置SPI功能引脚:SCK 时钟引脚 */
GPIO_InitStructure.GPIO_Pin = SPI_SCK_PIN;;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //复用推挽输出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(SPI_SCK_PORT, &GPIO_InitStructure); //初始化GPIO
/* 配置SPI功能引脚:MISO 主机输出从机输入引脚 */
GPIO_InitStructure.GPIO_Pin = SPI_MISO_PIN;;
GPIO_Init(SPI_SCK_PORT, &GPIO_InitStructure);
/* 配置SPI功能引脚:MOSI 主机输入从机输出引脚 */
GPIO_InitStructure.GPIO_Pin = SPI_MOSI_PIN;;
GPIO_Init(SPI_SCK_PORT, &GPIO_InitStructure);
/* SPI外设配置 --NSS 引脚由软件控制以及 MSB 先行模式*/
SPI_Cmd(SPI3, DISABLE); //失能能SPI外设
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_1Edge; //串行同步时钟的第二个跳变沿(上升或下降)数据被采样
SPI_InitStructure.SPI_NSS = SPI_NSS_Soft; //NSS信号由硬件(NSS管脚)还是软件(使用SSI位)管理:内部NSS信号有SSI位控制
SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_128; //定义波特率预分频的值:波特率预分频值
SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB; //指定数据传输从MSB位还是LSB位开始:数据传输从MSB位开始
SPI_InitStructure.SPI_CRCPolynomial = 7; //CRC值计算的多项式
SPI_Init(SPI3, &SPI_InitStructure); //根据SPI_InitStruct中指定的参数初始化外设SPIx寄存器
/* 配置SPI功能引脚:CS 串行Flash片选引脚 */
GPIO_InitStructure.GPIO_Pin = SPI_CS_PIN;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //推挽输出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(SPI_SCK_PORT, &GPIO_InitStructure);
/* 配置SPI所用的引脚:默认高电平 */
GPIO_SetBits(GPIOB,SPI_SCK_PIN|SPI_MISO_PIN|SPI_MOSI_PIN|SPI_CS_PIN);
SPI_Cmd(SPI3, ENABLE); //使能SPI外设
}
/**
* 函数功能: SPI3_接收发送数据
* 输入参数:
* 返 回 值:
* 说 明:无
*/
u8 Spi3_readwritebyte(u8 Txdata)
{
u8 retry=0;
while (SPI_I2S_GetFlagStatus(SPI3, SPI_I2S_FLAG_TXE) == RESET) //检查指定的SPI标志位设置与否:发送缓存空标志位
{
retry++;
if(retry>200)return 0;
}
SPI_I2S_SendData(SPI3, Txdata); //通过外设SPIx发送一个数据
retry=0;
while (SPI_I2S_GetFlagStatus(SPI3, SPI_I2S_FLAG_RXNE) == RESET) //检查指定的SPI标志位设置与否:接受缓存非空标志位
{
retry++;
if(retry>200)return 0;
}
return SPI_I2S_ReceiveData(SPI3); //返回通过SPIx最近接收的数据
}
/**
* 函数功能: Mcp3202_接收数据
* 输入参数:
* 返 回 值: Mcp3202_ADC_16位数据
* 说 明:
*/
int Mcp3202_read(void)
{
u32 p;
u16 Mcp3202_buffet[2]={0};
CS_LOW;
delay_us(5);
Mcp3202_buffet[0] = Spi3_readwritebyte(0xFF);
Mcp3202_buffet[1] = Spi3_readwritebyte(0xFF);
CS_HIGH;
p = (int)(((Mcp3202_buffet[0] << 8) | Mcp3202_buffet[1])&0xFFFF);
return (p);
}
/**
* 函数功能: 排序取值
* 输入参数: 1.要取得数值 2.要写入的排列数据多少
* 返 回 值: 参数1的数组的值
* 说 明:无
*/
u16 Get_Adc_Average(u8 ch,u8 times)
{
unsigned char ii =0,nn=0;
float adc_temp = 0;
float ad_temp[255] ={0};
for(ii=0;ii<times;ii++)
{
ad_temp[ii] =(float) Mcp3202_read();
}
for(ii=0;ii<times;ii++)
{
for(nn=0;nn<times;nn++)
{
if(ad_temp[nn] > ad_temp[nn+1])
{
adc_temp = ad_temp[nn];
ad_temp[nn] = ad_temp[nn+1];
ad_temp[nn+1] = adc_temp;
}
}
}
adc_temp = 0;
adc_temp = ad_temp[ch];
return adc_temp;
}