电子说
CS1237是一款高精度、低功耗Sigma-Delta模数转换芯片,内置一路Sigma-Delta ADC,一路差分输入通道和一路温度传感器,ADC采用两阶sigma delta 调制器,通过低噪声仪用放大器结构实现PGA放大,放大倍数可选:1、2、64、128。在PGA=128时,有效分辨率可达20位(工作在5V)。 CS1237内置RC振荡器,无需外置晶振。
CS1237可以通过DOUT DRDY/和SCLK进行多种功能模式的配置,例如用作温度检测、PGA选择、ADC数据输出速率选择等等。 具有Power down模式。
主要特性
ADC功能特性:
应用场景

| DVDD | 电源正极(3 - 5V) |
|---|---|
| DGND | 电源负极 |
| AVDD | 基准源输入正 |
| AGND | 基准源输入负 |
| SCLK | SPI时钟输入 |
| DOUT | SPI数据输入/输出 |
| A+ | 通道正输入 |
| A- | 通道负输入 |
基准源输入说明:基准电压可以由外部输入也可是内部输出,如果要使用外部基准电压,要先关闭内部基准,内部基准控制由寄存器(refo_off)控制。

读写参数配置过程简述,在DOUT DRDY/由高变低之后:

读数据
CS1237可以持续的转换模拟输入信号,当将DOUT DRDY/拉低后,表明数据已经准备好接受,输入的第一个SCLK来就可以将输出的最高位读出,在24个SCLK后将所有的24位数据读出,如果这时暂停SCLK的发送,DOUT DRDY/会保持着最后一位的数据,直到其被拉高,第25和26个SCLK输出配置寄存器是否有写操作标志,第25个SCLK对应的DOUT DRDY/为1时表明配置寄存器Config被写入了新的值,第26个SCLK对应的DOUT DRDY/为芯片扩展保留位,目前输出一直为0,通过第27个SCLK可以将DOUT DRDY/拉高,此后当DOUT DRDY/被再次拉低,表示新的数据已经准备好接受,进行下一个数据的转换。
数据输出格式
CS1237输出的数据为24位的2进制补码,最高位(MSB)最先输出。最小有效位(LSB)为(0.5VREF/Gain)/(2^23-1)。正值满幅输出码为7FFFFFH,负值满幅输出码为800000H。下表为不同模拟输入信号对应的理想输出码。
根据上表可知,不同的增益,输入信号Vin范围也不同。
| PGA | VIN+ - VIN- |
|---|---|
| 1 | ±1.25V |
| 2 | ±0.625V |
| 64 | ±0.01953125V |
| 128 | ±0.009765625V |
注意:因为 CS1237 内部的前置放大器和 Σ-Δ 调制器都需要一个共模电压范围 (Vcm),所以输入信号要满足以下三个条件,有一个条件不符合,输出可能就会不正常。
差分电压:|AIN+ - AIN-| ≤ Vref / 2 x Gain。
共模电压:Vcm = (AIN+ + AIN-) / 2,Vcm最好接近Vref / 2。
AIN+,AIN-的输入电压:DGND ≤ AINx ≤ DVDD。
读写参数配置,需要通过7bits 命令字去配置。
CS1237只有一组Config寄存器
电压的换算:
CS1237 是 24 位ADC,输出 24 位补码格式的数据,代表输入的差分电压:Vin(diff)=AIN+ − AIN−。
其输出码值范围为:
正满量程:理论输出码为+8,388,607 (0x7FFFFF),对应输入电压为+Vref / 2x增益。
负满量程:理论输出码为-8,388,608 (0x800000),对应输入电压为-Vref / 2x增益。
即换算有符号的数据公式:Vin(diff)=输出码值 / 2^23 x Vref / 2x增益。
温度的换算:
测量内部温度时,需要先校准再测量,芯片没有出厂校准,必须用两个已知温度点进行标定。注意:测量温度时,AIN+、AIN- 对外引脚无效,即采集不了输入信号。

STM32F103C8T6最小系统板,CS1237 ADC模数转换模块,OLED显示模块等
| STM32F103 | CS1237 |
|---|---|
| 3.3V | DVDD |
| GND | DGND |
| PA0 | SLCK |
| PA1 | DOUT |
| PB8 | OLED->SLC |
| PB9 | OLED->SDA |
| A + - A- | 差分信号 |
CS1237.c
#include "cs1237.h"
void CS1237_Init(void)
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
GPIO_ResetBits(GPIOA, GPIO_Pin_0);
}
void CS1237_DOUT_Input(void)
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
}
void CS1237_DOUT_Output(void)
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
}
int8_t CS1237_WirteByte(uint8_t data) //写配置
{
uint8_t i;
uint32_t t=0;
CS1237_DOUT_Input();
while (DOUT_READ != 0) //超时
{
if((t += 5) > CS1237_DEFAULT_TIMEOUT_US)
return -1;
}
for (i = 0; i < 26; i++) { //1-26 SCLK
CS1237_SCLK_H
Delay_us(2);
CS1237_SCLK_L
Delay_us(2);
}
CS1237_DOUT_Output(); //27 SCLK
CS1237_DOUT_H
CS1237_SCLK_H
Delay_us(2);
CS1237_SCLK_L
Delay_us(2);
for (i = 0; i < 2; i++) { //28-29 SCLK
CS1237_SCLK_H
Delay_us(2);
CS1237_SCLK_L
Delay_us(2);
}
for (i = 0; i < 7; i++) { //30-36 SCLK
if (CS1237_WRITE_REG & (0x40 > > i)) CS1237_DOUT_H
else CS1237_DOUT_L
CS1237_SCLK_H
Delay_us(2);
CS1237_SCLK_L
Delay_us(2);
}
CS1237_SCLK_H //37 SCLK
Delay_us(2);
CS1237_SCLK_L
Delay_us(2);
for (i = 0; i < 8; i++) { //38-45 SCLK
if (data & (0x80 > > i)) CS1237_DOUT_H
else CS1237_DOUT_L
CS1237_SCLK_H
Delay_us(2);
CS1237_SCLK_L
Delay_us(2);
}
CS1237_DOUT_H
CS1237_DOUT_Input(); //46 SCLK
CS1237_SCLK_H
Delay_us(2);
CS1237_SCLK_L
Delay_us(2);
return 0;
}
int8_t CS1237Reg(uint8_t *out) //读配置
{
uint8_t i;
uint32_t t=0;
uint8_t Data = 0x00;
CS1237_DOUT_Input();
while (DOUT_READ != 0) //超时
{
if((t += 5) > CS1237_DEFAULT_TIMEOUT_US)
return -1;
}
for (i = 0; i < 26; i++) { //1-26 SCLK
CS1237_SCLK_H
Delay_us(2);
CS1237_SCLK_L
Delay_us(2);
}
CS1237_DOUT_Output();
CS1237_DOUT_H
CS1237_SCLK_H //27 SCLK
Delay_us(2);
CS1237_SCLK_L
Delay_us(2);
for (i = 0; i < 2; i++) { //28-29 SCLK
CS1237_SCLK_H
Delay_us(2);
CS1237_SCLK_L
Delay_us(2);
}
for (i = 0; i < 7; i++) { //30-36 SCLK
if (CS1237_READ_REG & (0x40 > > i)) CS1237_DOUT_H
else CS1237_DOUT_L
CS1237_SCLK_H
Delay_us(2);
CS1237_SCLK_L
Delay_us(2);
}
CS1237_DOUT_Input(); //37 SCLK
CS1237_SCLK_H
Delay_us(2);
CS1237_SCLK_L
Delay_us(2);
for (i = 0; i < 8; i++) { //38-45 SCLK
CS1237_SCLK_H
Delay_us(2);
Data = ((Data < < 1) | (DOUT_READ ? 1 : 0));
CS1237_SCLK_L
Delay_us(2);
}
CS1237_DOUT_Output();
CS1237_DOUT_H
CS1237_SCLK_H
Delay_us(2);
CS1237_SCLK_L
Delay_us(2);
*out = Data;
return 0;
}
int8_t CS1237_ReadRaw24(int32_t *signed_out)
{
uint8_t i;
uint32_t raw = 0;
uint32_t t = 0;
CS1237_DOUT_Input();
while (DOUT_READ != 0) //超时
{
Delay_us(2);
if((t += 10) > CS1237_DEFAULT_TIMEOUT_US)
return -1;
}
for (i = 0; i < 24; i++) {
CS1237_SCLK_H
Delay_us(2);
raw = (raw < < 1) | (DOUT_READ ? 1 : 0);
CS1237_SCLK_L
Delay_us(2);
}
for (i = 0; i < 2; i++) {
CS1237_SCLK_H
Delay_us(2);
CS1237_SCLK_L
Delay_us(2);
}
raw &= 0xFFFFFFUL;
if (raw & (1UL < < 23)) *signed_out = (int32_t)(raw | 0xFF000000UL);
else *signed_out = (int32_t)raw;
return 0;
}
float CS1237_Temperature(uint32_t Yb)
{
int8_t error;
float tempB;
error = CS1237_WirteByte(0x02);
if(error != 0) OLED_ShowString(4, 1, "ERROR3");
tempB = ((float)Yb * (273.15f + CS1237_TEMPA)) / ((float)CS1237_YA - 273.15f); //单位℃
return tempB;
}
main.c
#include "stm32f10x.h"
#include "delay.h"
#include "oled.h"
#include "cs1237.h"
uint8_t status;
int32_t val;
float diff_V, Temp;
uint8_t gain_table[4] = {1, 2, 64, 128}; //设置不同增益,输入差分范围也不一样,1:±1.25V;2:±0.625V;64:±0.01953125V;128:±0.009765625V
int main(void)
{
OLED_Init();
CS1237_Init();
Delay_ms(100);
OLED_ShowString(1, 1, "CS1237");
OLED_ShowString(3, 4, "000.000V");
// OLED_ShowString(4, 4, "0000.00"); //温度显示
CS1237_WirteByte(0x00);
Delay_ms(100);
if(CS1237Reg(&status) == 0)
{
OLED_ShowString(1, 10, "0x");
OLED_ShowHexNum(1,12,status,2);
}else{
OLED_ShowString(1, 10, "ERROR1");
}
while (1)
{
if (CS1237_ReadRaw24(&val) == 0) {
// OLED_ShowSignedNum(4,1,val,7);
//
diff_V = ((float)val / 8388607.0f) * (1.25f / (float)gain_table[(status > >2) & 0x03]);
OLED_ShowSignedFloat(3,4,diff_V,2,3);
Delay_ms(100);
} else {
OLED_ShowString(3,4," ERROR2 ");
}
//温度检测
// Temp = CS1237_Temperature(val);
// OLED_ShowSignedFloat(4,4,Temp,3,2);
// Delay_ms(100);
}
}
图一:A+接1.2V,A-接1.496V;图二:A+接1.815V,A-接1.2V。
Q:CS1237/CS1238 主要适用领域有哪些?
A:CS1237/CS1238是针对桥式传感器的低成本解决方案。一般应用于称重测量、压力测量等细分领域。如下图所示:
Q:除了桥式传感器,CS1237/CS1238 还适用于其它应用领域吗?
A:取决于 AINP/AINN 端口的差分信号范围以及共模电压范围是否满足 Datasheet 的要求。如下图所示:
Q:CS1237/CS1238 是否有单次转换模式?
A:否。CS1237/CS1238 只有连续转换模式。
Q:CS1237/CS1238 是否可以单端输入模式(AINN 接地)?
A:如问题 2 所述,输入信号需要满足共模与差模范围,一般情况下不建议单端输入的应用使用该系列产品。当 PGA=64/128 时,不允许 AINP 或 AINN 直接接地,否则测量信号异常;当PGA=1/2 时,由于 Buffer 开启的缘故,单端输入阻抗不宜太大(建议前端电路的输出阻抗几十欧姆以下),否则会影响线性。
Q:CS1237/CS1238 的工作电压范围是多少?为什么数据手册里面描述 4.5-5.5V、3.0V-3.6V 的?
A: 1、工作电压范围是 2.7V~5.5V。
2、数据手册描述的是两个典型电压值(5V/3.3V)的工作电流,并不是只能工作在这两个电压区间。
Q:CS1237/CS1238 上电默认配置是什么?
A:上电默认配置是 PGA=128、DataRate=10Hz。
Q:CS1237/CS1238 的外置参考电压是否可以高于 VDD?
A:不可以。外置参考电压需满足条件:REFIN=1.5V~VDD。举例:桥式传感器的应用不允许传感器激励源以及参考输入使用 5.0V,而 VDD 使用 3.3V。
Q:在桥式传感器应用里,为什么 REFIN 与 REFOUT 连在一起?
A:使用传感器的激励源作为 ADC 的参考源,(桥式传感器应用)可以有效抑制漂移,降低系统对参考源的要求。其中 REFIN 为参考源输入,REFOUT 为传感器激励源。对于一般应用,两者通常连在一起,可以通过 REFOUT 引脚控制激励源的开启/关闭。如果系统使用外部激励源/参考源,则悬空 REFOUT 引脚即可,因此并不规定 REFIN 引脚必须与REFOUT 引脚连在一起。
Q:CS1237/CS1238 是否内置参考电压源?
A:否。CS1237/CS1238 的 REFOUT 引脚输出的激励信号为 VDD,起到控制激励源的作用。如有必要可以进入休眠模式,关闭激励源节省桥式传感器功耗。如下图所示:
Q:实际应用中,多个称重传感器应该怎么与 ADC 连接?
A:如果传感器是测量同一物体(例如:厨房垃圾处理器),一般建议使用并联的方式。则相同类型的信号线连接在一起。对于传感器的要求是产品规格尽量一致,尤其是灵敏度需要一致,否则会导致偏载问题。如果使用 REFOUT 引脚输出 VS,需要注意传感器的激励电流是否在合理范围内。举例:
如果系统不是测量同一个物体,则传感器单独连接不同的模拟通道或不同的 ADC。
最后,如需源码,点赞收藏+关注,评论留言!!!
审核编辑 黄宇
全部0条评论
快来发表一下你的评论吧 !