完整教程下载地址:【安富莱】STM32H7用户手册发布,重在BSP驱动包设计方法,HAL库的框架学习,授人以渔,更新至94章(2021-11-29) - STM32H7 - 硬汉嵌入式论坛 - Powered by Discuz!
本章节为大家讲解8通道24bit ADC芯片驱动实现。
目录
93.1 初学者重要提示
93.2 ADC结构分类
93.2.1 SAR ADC(逐次逼近型)
93.2.2 Sigma-Delta ADC
93.2.3 Integrating ADC
93.2.4 FLASH ADC
93.2.5 Pipelined ADC
93.2.6 Two Step ADC
93.3 ADS1256硬件设计
93.3.1 ADS1256硬件接口
93.3.2 ADS1256模块
93.4 ADS1256关键知识点整理(重要)
93.4.1 ADS1256基础信息
93.4.2 ADS1256常用引脚的作用
93.4.3 ADS1256输出电压计算公式
93.4.4 ADS1256时序图
93.4.5 ADS1256的增益和测量范围问题
93.4.6 ADS1256输入缓冲器
93.4.7 ADS1256支持的采样率
93.4.8 ADS1256的多路选择器,单端和多端输入
93.5 ADS1256驱动设计
93.5.1 第1步,ADS1256所涉及到的GPIO配置
93.5.2 第2步,ADS1256的8bit读写函数实现
93.5.3 第3步,ADS1256的24bit ADC数据读取
93.5.4 第3步,ADS1256增益和采样率配置
93.5.5 第4步,ADS1256启动采样
93.5.6 第5步,ADS1256的中断处理(8通道数据读取)
93.6 ADS1256板级支持包(bsp_spi_ads1256.c)
93.6.1 函数bsp_InitADS1256
93.6.2 函数ADS1256_CfgADC
93.6.3 函数ADS1256_StartScan
93.6.4 函数ADS1256_SetChannal
93.7 ADS1256实际测量效果(10uV抖动)
93.8 ADS1256驱动移植和使用
93.9 实验例程设计框架
93.10 实验例程说明(MDK)
93.11 实验例程说明(IAR)
93.12 总结
这里将六种DAC结构为大家做个普及。注,这些知识翻译自美信和TI的英文技术手册。
逐次逼近型ADC通常是中高分辨率的首选架构,采样速率通常低于5Msps。SAR ADC最常见的分辨率范围是8位到20位,并具有低功耗和小尺寸的特点。这种组合使其非常适合各种应用,例如自动测试设备,电池供电的设备,数据采集系统,医疗仪器,电机和过程控制,工业自动化,电信,测试和测量,便携式系统,高速闭环系统和窄带接收器。
Sigma-delta ADC主要用于低速应用中,该应用需要通过过采样来权衡速度和分辨率,然后进行滤波以降低噪声。24位sigma-delta转换器用于自动化测试设备,高精度便携式传感器,医疗和科学仪器以及地震数据采集等应用中。
集成ADC提供高分辨率,并且可以提供良好的线路频率和噪声抑制。集成架构提供了一种新颖且直接的方法,可将低带宽模拟信号转换为数字表示形式。这些类型的转换器通常包括用于LCD或LED显示器的内置驱动器,并且在许多便携式仪器应用中都可以找到,包括数字面板表和数字万用表。
Flash ADC是将模拟信号转换为数字信号的最快方法。它们适用于需要非常大带宽的应用。然而,闪存转换器功率高,具有相对较低的分辨率,并且可能非常昂贵。这将它们限制在通常无法以其他任何方式解决的高频应用中。示例包括数据采集,卫星通信,雷达处理,示波器和高密度磁盘驱动器。
流水线ADC已成为最受欢迎的ADC体系结构,其采样率从每秒几兆采样(MS / s)到最高100MS / s +,分辨率为8至16位。它们提供的分辨率和采样率,可覆盖各种应用,包括CCD成像,超声医学成像,数字接收器,基站,数字视频(例如HDTV),xDSL,电缆调制解调器和快速以太网。
两步ADC也称为子范围转换器,有时也称为多步或half flash(比Flash架构慢)。这是Flash ADC和流水线ADC的交叉点。与Flash ADC相比,可以实现更高的分辨率或更小的裸片尺寸。
这里将开发板上的ADS1256硬件接口,ADS1256模块为大家做个说明。
ADS1256的原理图论坛下载:链接 。
V7板子上ADS1256模块的插座的原理图如下:
实际对应开发板的位置如下:
为了方便大家更好的理解接线,下面是框图:
产品规格:
1、 单电源5.0V DC供电,提供正负5V信号采样功能。
2、 MCU接口:SPI。
3、 主ADC芯片:ADS1256 (全新进口原装正品)。
4、 电压基准,之前采用的LM285-2.5,现在采用的REF5025(全新进口原装正品)。
5、 输入电路带分压电阻和R-C滤波,方便客户自己变更增益范围。
6、 芯片内带可编程增益放大器,增益范围:1-64倍。
7、 芯片内部输入带缓冲放大器,可以直接连接传感器。
正面:
反面:
接线图:
驱动ADS1256需要对下面这些知识点有个认识。
ADS1256是TI公司推出的微功耗、高精度、8通道、 24位△-∑型高性能模数转换器(ADC)。该器件提供高达23比特的无噪声精度、数据速率高达30kSPS(次采样/秒)、0.0010%非线性特性(最大值)以及众多的板上外设(输入模拟多路开关、输入缓冲器、可编程增益放大器和可编程数字滤波器等),可为设计人员带来完整而高分辨率的量测解决方案。
ADS1256的封装形式:
这里把常用的几个引脚做个说明:
模拟电源供电。
模拟地。
负参考电压输入。
正参考电压输入。
模拟公共输入。
模拟输入通道0到通道7
同步,掉电输入。
复位引脚。
数字电源。
数字地。
晶振输入端。
片选输入端。
数据就绪输出信号引脚。
数据输出。
数据输入。
时钟引脚。
通用GPIO
ADS1256的计算公式如下:
最小单位值是2 * VREF/(PGA * (2^23 − 1))
采用二进制补码表示(其实就是24bit有符号数,我们将转换结果定义为int32_t即可)。
驱动ADS1256主要是两个时序图需要了解,读写时序:
外部晶振频率 = 7.68MHz,
时钟频率 tCLK = 1/7.68M = 0.13uS
输出数据周期 tDATA = 1 / 30K = 0.033mS (按30Ksps计算)
对SPI的时钟速度要求:
最快 4个tCLK = 0.52uS
最慢 10个tDATA = 0.3mS (按 30Ksps 计算)
SCL高电平和低电平持续时间最小 200ns。
数据读取流程:
这个过程中特别注意读取的是上次转换的数据。
ADS1256的增益和测量范围关系如下:
比如增益是1时,测量范围是正负5V,增益是64时,测量范围是正负78.125mV。
开关输入缓冲器时,影响到的几个参数,大家需要做个了解。
开缓冲的情况下,输入参考值噪声。
关闭缓冲时的输入参考噪声:
开缓冲的情况下,有效分辨率:
关闭缓冲时的有效位数:
打开缓冲器后的输入阻抗:
关闭缓冲器后的输入阻抗:
ADS1256支持的采样率如下,这里特别注意,因为切换通道和读数据耗时 123微秒, 因此扫描中断模式工作时,最大速率 = DRATE_1000SPS。
ADS1256的8路支持是通过多路选择器实现,8路采样时是选择相应通道进行采样:
实际应用的的8路单端采样和4路差分采样效果如下:
ADS1256的程序驱动框架设计如下:
有了这个框图,程序设计就比较好理解了。
这里需要把用到的GPIO时钟、GPIO引脚配置好:
/*
ADS1256模块 STM32-V7开发板(示波器接口)
+5V <------ 5.0V 5V供电
GND ------- GND 地
DRDY ------> PC6 准备就绪
CS <------ PC7 SPI_CS
DIN <------ PG10 SPI_MOSI
DOUT ------> PA5 SPI_MISO
SCLK <------ PA4 SPI时钟
GND ------- GND 地
PDWN <------ PB7 掉电控制
RST <------ PC3 复位信号
NC 空脚
NC 空脚
*/
#ifdef SOFT_SPI /* 软件SPI */
/* 定义GPIO端口 */
#define SCK_CLK_ENABLE() __HAL_RCC_GPIOA_CLK_ENABLE()
#define SCK_GPIO GPIOA
#define SCK_PIN GPIO_PIN_4
#define SCK_1() SCK_GPIO->BSRR = SCK_PIN
#define SCK_0() SCK_GPIO->BSRR = ((uint32_t)SCK_PIN << 16U)
#define DIN_CLK_ENABLE() __HAL_RCC_GPIOG_CLK_ENABLE()
#define DIN_GPIO GPIOG
#define DIN_PIN GPIO_PIN_10
#define DIN_1() DIN_GPIO->BSRR = DIN_PIN
#define DIN_0() DIN_GPIO->BSRR = ((uint32_t)DIN_PIN << 16U)
#define CS_CLK_ENABLE() __HAL_RCC_GPIOC_CLK_ENABLE()
#define CS_GPIO GPIOC
#define CS_PIN GPIO_PIN_7
#define CS_1() CS_GPIO->BSRR = CS_PIN
#define CS_0() CS_GPIO->BSRR = ((uint32_t)CS_PIN << 16U)
#define DOUT_CLK_ENABLE() __HAL_RCC_GPIOA_CLK_ENABLE()
#define DOUT_GPIO GPIOA
#define DOUT_PIN GPIO_PIN_5
#define DOUT_IS_HIGH() ((DOUT_GPIO->IDR & DOUT_PIN) != 0)
#define DRDY_CLK_ENABLE() __HAL_RCC_GPIOC_CLK_ENABLE()
#define DRDY_GPIO GPIOC
#define DRDY_PIN GPIO_PIN_6
#define DRDY_IS_LOW() ((DRDY_GPIO->IDR & DRDY_PIN) == 0)
#define DRDY_IRQn EXTI9_5_IRQn
#define DRDY_IRQHandler EXTI9_5_IRQHandler
/* PDWN <------ PB7 掉电控制 */
#define PWDN_CLK_ENABLE() __HAL_RCC_GPIOB_CLK_ENABLE()
#define PWDN_GPIO GPIOB
#define PWDN_PIN GPIO_PIN_7
#define PWDN_1() PWDN_GPIO->BSRR = PWDN_PIN
#define PWDN_0() PWDN_GPIO->BSRR = ((uint32_t)PWDN_PIN << 16U)
/* RST <------ PC3 复位信号 */
#define RST_CLK_ENABLE() __HAL_RCC_GPIOC_CLK_ENABLE()
#define RST_GPIO GPIOC
#define RST_PIN GPIO_PIN_3
#define RST_1() RST_GPIO->BSRR = RST_PIN
#define RST_0() RST_GPIO->BSRR = ((uint32_t)RST_PIN << 16U)
#endif
/*
*********************************************************************************************************
* 函 数 名: bsp_InitADS1256
* 功能说明: 配置STM32的GPIO和SPI接口,用于连接 ADS1256
* 形 参: 无
* 返 回 值: 无
*********************************************************************************************************
*/
void bsp_InitADS1256(void)
{
#ifdef SOFT_SPI
GPIO_InitTypeDef gpio_init;
RST_1();
PWDN_1();
CS_1();
SCK_0(); /* SPI总线空闲时,钟线是低电平 */
DIN_1();
/* 打开GPIO时钟 */
SCK_CLK_ENABLE();
DIN_CLK_ENABLE();
CS_CLK_ENABLE();
DOUT_CLK_ENABLE();
DRDY_CLK_ENABLE();
PWDN_CLK_ENABLE();
RST_CLK_ENABLE();
/* 配置几个推完输出IO */
gpio_init.Mode = GPIO_MODE_OUTPUT_PP; /* 设置推挽输出 */
gpio_init.Pull = GPIO_NOPULL; /* 上下拉电阻不使能 */
gpio_init.Speed = GPIO_SPEED_FREQ_HIGH; /* GPIO速度等级 */
gpio_init.Pin = SCK_PIN;
HAL_GPIO_Init(SCK_GPIO, &gpio_init);
gpio_init.Pin = DIN_PIN;
HAL_GPIO_Init(DIN_GPIO, &gpio_init);
gpio_init.Pin = CS_PIN;
HAL_GPIO_Init(CS_GPIO, &gpio_init);
gpio_init.Pin = PWDN_PIN;
HAL_GPIO_Init(PWDN_GPIO, &gpio_init);
/* DRDY 设置为输入 */
gpio_init.Mode = GPIO_MODE_INPUT; /* 设置输入 */
gpio_init.Pull = GPIO_NOPULL; /* 上下拉电阻不使能 */
gpio_init.Speed = GPIO_SPEED_FREQ_HIGH; /* GPIO速度等级 */
gpio_init.Pin = DRDY_PIN;
HAL_GPIO_Init(DRDY_GPIO, &gpio_init);
gpio_init.Pin = DOUT_PIN;
HAL_GPIO_Init(DOUT_GPIO, &gpio_init);
#endif
}
这里重点注意DRDY转换就绪引脚的配置,DRDY_IRQn和DRDY_IRQHandler不要配置错了。
读写函数实现如下:
/*
*********************************************************************************************************
* 函 数 名: ADS1256_Send8Bit
* 功能说明: 向SPI总线发送8个bit数据。 不带CS控制。
* 形 参: _data : 数据
* 返 回 值: 无
*********************************************************************************************************
*/
static void ADS1256_Send8Bit(uint8_t _data)
{
uint8_t i;
/* 连续发送多个字节时,需要延迟一下 */
ADS1256_DelaySCLK();
ADS1256_DelaySCLK();
/* ADS1256 要求 SCL高电平和低电平持续时间最小 200ns */
for(i = 0; i < 8; i++)
{
if (_data & 0x80)
{
DIN_1();
}
else
{
DIN_0();
}
SCK_1();
ADS1256_DelaySCLK();
_data <<= 1;
SCK_0(); /* <---- ADS1256 是在SCK下降沿采样DIN数据, 数据必须维持 50nS */
ADS1256_DelaySCLK();
}
}
/*
*********************************************************************************************************
* 函 数 名: ADS1256_Recive8Bit
* 功能说明: 从SPI总线接收8个bit数据。 不带CS控制。
* 形 参: 无
* 返 回 值: 无
*********************************************************************************************************
*/
static uint8_t ADS1256_Recive8Bit(void)
{
uint8_t i;
uint8_t read = 0;
ADS1256_DelaySCLK();
/* ADS1256 要求 SCL高电平和低电平持续时间最小 200ns */
for (i = 0; i < 8; i++)
{
SCK_1();
ADS1256_DelaySCLK();
read = read<<1;
SCK_0();
if (DOUT_IS_HIGH())
{
read++;
}
ADS1256_DelaySCLK();
}
return read;
}
读写的实现完全按照下面的时序实现:
这里主要注意时间实现:
t1:SPI时钟周期:
外部晶振频率 = 7.68MHz,
时钟频率 tCLK = 1/7.68M = 0.13uS
输出数据周期 tDATA = 1 / 30K = 0.033mS (按30Ksps计算)
对SPI的时钟速度要求:
最快 4个tCLK = 0.52uS
最慢 10个tDATA = 0.3mS (按 30Ksps 计算)
t2H,t2L :脉冲高低电平:
SCL高电平和低电平持续时间最小 200ns。
实现代码如下:
/*
*********************************************************************************************************
* 函 数 名: ADS1256_ReadData
* 功能说明: 读ADC数据
* 形 参: 无
* 返 回 值: 无
*********************************************************************************************************
*/
static int32_t ADS1256_ReadData(void)
{
uint32_t read = 0;
CS_0(); /* SPI片选 = 0 */
ADS1256_Send8Bit(CMD_RDATA); /* 读数据的命令 */
ADS1256_DelayDATA(); /* 必须延迟才能读取芯片返回数据 */
/* 读采样结果,3个字节,高字节在前 */
read = ADS1256_Recive8Bit() << 16;
read += ADS1256_Recive8Bit() << 8;
read += ADS1256_Recive8Bit() << 0;
CS_1(); /* SPI片选 = 1 */
/* 负数进行扩展。24位有符号数扩展为32位有符号数 */
if (read & 0x800000)
{
read += 0xFF000000;
}
return (int32_t)read;
}
这段代码里面关键是24bit数据的补码处理。对负数进行扩展,24位有符号数扩展为32位有符号数。
代码如下:
/*
*********************************************************************************************************
* 函 数 名: ADS1256_CfgADC
* 功能说明: 配置ADC参数,增益和数据输出速率
* 形 参: _gain : 支持增益参数。
* ADS1256_GAIN_1
* ADS1256_GAIN_2
* ADS1256_GAIN_4
* ADS1256_GAIN_8
* ADS1256_GAIN_16
* ADS1256_GAIN_32
* ADS1256_GAIN_64
*
* _drate : 数据输出速率,不推荐超过1000SPS
* ADS1256_30000SPS
* ADS1256_15000SPS
* ADS1256_7500SPS
* ADS1256_3750SPS
* ADS1256_2000SPS
* ADS1256_1000SPS
* ADS1256_500SPS
* ADS1256_100SPS
* ADS1256_60SPS
* ADS1256_50SPS
* ADS1256_30SPS
* ADS1256_25SPS
* ADS1256_15SPS
* ADS1256_10SPS
* ADS1256_5SPS
* ADS1256_2d5SPS
* 返 回 值: 无
*********************************************************************************************************
*/
void ADS1256_CfgADC(ADS1256_GAIN_E _gain, ADS1256_DRATE_E _drate)
{
g_tADS1256.Gain = _gain;
g_tADS1256.DataRate = _drate;
ADS1256_StopScan(); /* 暂停CPU中断 */
ADS1256_ResetHard(); /* 硬件复位 */
ADS1256_WaitDRDY();
{
uint8_t buf[4]; /* 暂存ADS1256 寄存器配置参数,之后连续写4个寄存器 */
buf[0] = (0 << 3) | (1 << 2) | (1 << 1);
buf[1] = 0x08; /* 高四位0表示AINP接 AIN0, 低四位8表示 AINN 固定接 AINCOM */
buf[2] = (0 << 5) | (0 << 2) | (_gain << 1);
/* 因为切换通道和读数据耗时 123uS, 因此扫描中断模式工作时,最大速率 = DRATE_1000SPS */
buf[3] = s_tabDataRate[_drate]; // DRATE_10SPS; /* 选择数据输出速率 */
CS_0(); /* SPI片选 = 0 */
ADS1256_Send8Bit(CMD_WREG | 0); /* 写寄存器的命令, 并发送寄存器地址 */
ADS1256_Send8Bit(0x03); /* 寄存器个数 - 1, 此处3表示写4个寄存器 */
ADS1256_Send8Bit(buf[0]); /* 设置状态寄存器 */
ADS1256_Send8Bit(buf[1]); /* 设置输入通道参数 */
ADS1256_Send8Bit(buf[2]); /* 设置ADCON控制寄存器,增益 */
ADS1256_Send8Bit(buf[3]); /* 设置输出数据速率 */
CS_1(); /* SPI片选 = 1 */
}
bsp_DelayUS(50);
}
这个函数主要配置了4个ADS1256寄存器。
程序中的配置为:buf[0] = (0 << 3) | (1 << 2) | (1 << 1) ,意思是LSB传输,自动校准,使能模拟输入缓冲。
程序中的配置为:buf[1] = 0x08, 高四位0表示AINp接通的 AIN0, 低四位8表示 AINn接通的 AINCOM。AINp和AINn表示当前的多路选择器选通的AIN0:
程序中的配置为:buf[2] = (0 << 5) | (0 << 2) | (_gain << 1), 意思是关闭CLKOUT引脚输出,关闭传感器检测,设置增益为形参_gain。
程序中的配置为:buf[3] = s_tabDataRate[_drate],用于设置波特率:
static const uint8_t s_tabDataRate[ADS1256_DRATE_MAX] =
{
0xF0, /* 复位时缺省值 */
0xE0,
0xD0,
0xC0,
0xB0,
0xA1,
0x92,
0x82,
0x72,
0x63,
0x53,
0x43,
0x33,
0x20,
0x13,
0x03
};
typedef enum
{
ADS1256_30000SPS = 0,
ADS1256_15000SPS,
ADS1256_7500SPS,
ADS1256_3750SPS,
ADS1256_2000SPS,
ADS1256_1000SPS,
ADS1256_500SPS,
ADS1256_100SPS,
ADS1256_60SPS,
ADS1256_50SPS,
ADS1256_30SPS,
ADS1256_25SPS,
ADS1256_15SPS,
ADS1256_10SPS,
ADS1256_5SPS,
ADS1256_2d5SPS,
ADS1256_DRATE_MAX
}ADS1256_DRATE_E;
代码实现如下:
/*
*********************************************************************************************************
* 函 数 名: ADS1256_StartScan
* 功能说明: 将 DRDY引脚 (PC6 )配置成外部中断触发方式, 中断服务程序中扫描8个通道的数据。
* 形 参: 无
* 返 回 值: 无
*********************************************************************************************************
*/
void ADS1256_StartScan(void)
{
/* PC6 外部中断,BUSY
配置 BUSY 作为中断输入口,下降沿触发 */
{
GPIO_InitTypeDef GPIO_InitStructure;
DRDY_CLK_ENABLE(); /* 打开GPIO时钟 */
GPIO_InitStructure.Mode = GPIO_MODE_IT_FALLING;
GPIO_InitStructure.Pull = GPIO_NOPULL;
GPIO_InitStructure.Pin = DRDY_PIN;
HAL_GPIO_Init(DRDY_GPIO, &GPIO_InitStructure);
HAL_NVIC_SetPriority(DRDY_IRQn, 2, 0);
HAL_NVIC_EnableIRQ(DRDY_IRQn);
}
/* 开始扫描前, 清零结果缓冲区 */
{
uint8_t i;
g_tADS1256.Channel = 0;
for (i = 0; i < 8; i++)
{
g_tADS1256.AdcNow[i] = 0;
}
}
}
代码比较简单,主要是配置PC6的EXTI外部中断,并初始化变量。
代码如下:
/*
*********************************************************************************************************
* 函 数 名: EXTI9_5_IRQHandler
* 功能说明: 外部中断服务程序. 此程序执行时间约 123uS
* 形 参:无
* 返 回 值: 无
*********************************************************************************************************
*/
#ifdef EXTI9_5_ISR_MOVE_OUT /* bsp.h 中定义此行,表示本函数移到 stam32f4xx_it.c。 避免重复定义 */
void EXTI9_5_IRQHandler(void)
{
HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_6);
}
/*
*********************************************************************************************************
* 函 数 名: EXTI9_5_IRQHandler
* 功能说明: 外部中断服务程序入口。PI6 / AD7606_BUSY 下降沿中断触发
* 形 参: 无
* 返 回 值: 无
*********************************************************************************************************
*/
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
if (GPIO_Pin == GPIO_PIN_6)
{
ADS1256_ISR();
}
}
#endif
/*
*********************************************************************************************************
* 函 数 名: ADS1256_ISR
* 功能说明: 定时采集中断服务程序
* 形 参: 无
* 返 回 值: 无
*********************************************************************************************************
*/
void ADS1256_ISR(void)
{
/* 读取采集结构,保存在全局变量 */
ADS1256_SetChannal(g_tADS1256.Channel); /* 切换模拟通道 */
bsp_DelayUS(5);
ADS1256_WriteCmd(CMD_SYNC);
bsp_DelayUS(5);
ADS1256_WriteCmd(CMD_WAKEUP);
bsp_DelayUS(25);
if (g_tADS1256.Channel == 0)
{
g_tADS1256.AdcNow[7] = ADS1256_ReadData(); /* 注意保存的是上一个通道的数据 */
}
else
{
g_tADS1256.AdcNow[g_tADS1256.Channel-1] = ADS1256_ReadData(); /* 注意保存的是上一个通道的数据 */
}
if (++g_tADS1256.Channel >= 8)
{
g_tADS1256.Channel = 0;
}
}
中断服务程序的代码完全是按照下面的时序时序:
这个过程中特别注意读取的是上次转换的数据。
ADS1256驱动文件bsp_spi_ads1256.c主要实现了如下几个API供用户调用:
函数原型:
void bsp_InitADS1256(void)
函数描述:
主要用于ADS1256的初始化。
函数原型:
void ADS1256_CfgADC(ADS1256_GAIN_E _gain, ADS1256_DRATE_E _drate)
函数描述:
用于配置ADS1256的增益和采样率。
函数参数:
ADS1256_GAIN_1
ADS1256_GAIN_2
ADS1256_GAIN_4
ADS1256_GAIN_8
ADS1256_GAIN_16
ADS1256_GAIN_32
ADS1256_GAIN_64
ADS1256_30000SPS
ADS1256_15000SPS
ADS1256_7500SPS
ADS1256_3750SPS
ADS1256_2000SPS
ADS1256_1000SPS
ADS1256_500SPS
ADS1256_100SPS
ADS1256_60SPS
ADS1256_50SPS
ADS1256_30SPS
ADS1256_25SPS
ADS1256_15SPS
ADS1256_10SPS
ADS1256_5SPS
ADS1256_2d5SPS
函数原型:
void ADS1256_StartScan(void)
函数描述:
此函数用于启动扫描,采样的中断方式。
函数原型:
static void ADS1256_SetChannal(uint8_t _ch)
函数描述:
此函数用于设置单端采样的通道。
函数参数:
测试LM285-2.5V稳压效果,抖动40uV:
测试干电池效果,抖动10uV左右,注意,这个级别的抖动容易受环境温度的影响,特别是开关空调,最明显。
移植步骤如下:
/* 定义GPIO端口 */
#define SCK_CLK_ENABLE() __HAL_RCC_GPIOA_CLK_ENABLE()
#define SCK_GPIO GPIOA
#define SCK_PIN GPIO_PIN_4
#define SCK_1() SCK_GPIO->BSRR = SCK_PIN
#define SCK_0() SCK_GPIO->BSRR = ((uint32_t)SCK_PIN << 16U)
#define DIN_CLK_ENABLE() __HAL_RCC_GPIOG_CLK_ENABLE()
#define DIN_GPIO GPIOG
#define DIN_PIN GPIO_PIN_10
#define DIN_1() DIN_GPIO->BSRR = DIN_PIN
#define DIN_0() DIN_GPIO->BSRR = ((uint32_t)DIN_PIN << 16U)
#define CS_CLK_ENABLE() __HAL_RCC_GPIOC_CLK_ENABLE()
#define CS_GPIO GPIOC
#define CS_PIN GPIO_PIN_7
#define CS_1() CS_GPIO->BSRR = CS_PIN
#define CS_0() CS_GPIO->BSRR = ((uint32_t)CS_PIN << 16U)
#define DOUT_CLK_ENABLE() __HAL_RCC_GPIOA_CLK_ENABLE()
#define DOUT_GPIO GPIOA
#define DOUT_PIN GPIO_PIN_5
#define DOUT_IS_HIGH() ((DOUT_GPIO->IDR & DOUT_PIN) != 0)
#define DRDY_CLK_ENABLE() __HAL_RCC_GPIOC_CLK_ENABLE()
#define DRDY_GPIO GPIOC
#define DRDY_PIN GPIO_PIN_6
#define DRDY_IS_LOW() ((DRDY_GPIO->IDR & DRDY_PIN) == 0)
#define DRDY_IRQn EXTI9_5_IRQn
#define DRDY_IRQHandler EXTI9_5_IRQHandler
/* PDWN <------ PB7 掉电控制 */
#define PWDN_CLK_ENABLE() __HAL_RCC_GPIOB_CLK_ENABLE()
#define PWDN_GPIO GPIOB
#define PWDN_PIN GPIO_PIN_7
#define PWDN_1() PWDN_GPIO->BSRR = PWDN_PIN
#define PWDN_0() PWDN_GPIO->BSRR = ((uint32_t)PWDN_PIN << 16U)
/* RST <------ PC3 复位信号 */
#define RST_CLK_ENABLE() __HAL_RCC_GPIOC_CLK_ENABLE()
#define RST_GPIO GPIOC
#define RST_PIN GPIO_PIN_3
#define RST_1() RST_GPIO->BSRR = RST_PIN
#define RST_0() RST_GPIO->BSRR = ((uint32_t)RST_PIN << 16U)
/*
*********************************************************************************************************
* 函 数 名: EXTI9_5_IRQHandler
* 功能说明: 外部中断服务程序. 此程序执行时间约 123uS
* 形 参:无
* 返 回 值: 无
*********************************************************************************************************
*/
#ifdef EXTI9_5_ISR_MOVE_OUT /* bsp.h 中定义此行,表示本函数移到 stam32f4xx_it.c。 避免重复定义 */
void EXTI9_5_IRQHandler(void)
{
HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_6);
}
/*
*********************************************************************************************************
* 函 数 名: EXTI9_5_IRQHandler
* 功能说明: 外部中断服务程序入口。PI6 / AD7606_BUSY 下降沿中断触发
* 形 参: 无
* 返 回 值: 无
*********************************************************************************************************
*/
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
if (GPIO_Pin == GPIO_PIN_6)
{
ADS1256_ISR();
}
}
#endif
通过程序设计框架,让大家先对配套例程有一个全面的认识,然后再理解细节,本次实验例程的设计框架如下:
第1阶段,上电启动阶段:
第2阶段,进入main函数:
配套例子:
V7-068_ADS1256(8通道带PGA的24位ADC)
实验目的:
重要提示:
实验内容:
上电后串口打印的信息:
波特率 115200,数据位 8,奇偶校验位无,停止位 1。
模块插入位置:
程序设计:
系统栈大小分配:
RAM空间用的DTCM:
硬件外设初始化
硬件外设的初始化是在 bsp.c 文件实现:
/*
*********************************************************************************************************
* 函 数 名: EXTI9_5_IRQHandler
* 功能说明: 外部中断服务程序. 此程序执行时间约 123uS
* 形 参:无
* 返 回 值: 无
*********************************************************************************************************
*/
#ifdef EXTI9_5_ISR_MOVE_OUT /* bsp.h 中定义此行,表示本函数移到 stam32f4xx_it.c。 避免重复定义 */
void EXTI9_5_IRQHandler(void)
{
HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_6);
}
/*
*********************************************************************************************************
* 函 数 名: EXTI9_5_IRQHandler
* 功能说明: 外部中断服务程序入口。PI6 / AD7606_BUSY 下降沿中断触发
* 形 参: 无
* 返 回 值: 无
*********************************************************************************************************
*/
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
if (GPIO_Pin == GPIO_PIN_6)
{
ADS1256_ISR();
}
}
#endif
MPU配置和Cache配置:
数据Cache和指令Cache都开启。配置了AXI SRAM区(本例子未用到AXI SRAM)和FMC的扩展IO区。
/*
*********************************************************************************************************
* 函 数 名: MPU_Config
* 功能说明: 配置MPU
* 形 参: 无
* 返 回 值: 无
*********************************************************************************************************
*/
static void MPU_Config( void )
{
MPU_Region_InitTypeDef MPU_InitStruct;
/* 禁止 MPU */
HAL_MPU_Disable();
/* 配置AXI SRAM的MPU属性为Write back, Read allocate,Write allocate */
MPU_InitStruct.Enable = MPU_REGION_ENABLE;
MPU_InitStruct.BaseAddress = 0x24000000;
MPU_InitStruct.Size = MPU_REGION_SIZE_512KB;
MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS;
MPU_InitStruct.IsBufferable = MPU_ACCESS_BUFFERABLE;
MPU_InitStruct.IsCacheable = MPU_ACCESS_CACHEABLE;
MPU_InitStruct.IsShareable = MPU_ACCESS_NOT_SHAREABLE;
MPU_InitStruct.Number = MPU_REGION_NUMBER0;
MPU_InitStruct.TypeExtField = MPU_TEX_LEVEL1;
MPU_InitStruct.SubRegionDisable = 0x00;
MPU_InitStruct.DisableExec = MPU_INSTRUCTION_ACCESS_ENABLE;
HAL_MPU_ConfigRegion(&MPU_InitStruct);
/* 配置FMC扩展IO的MPU属性为Device或者Strongly Ordered */
MPU_InitStruct.Enable = MPU_REGION_ENABLE;
MPU_InitStruct.BaseAddress = 0x60000000;
MPU_InitStruct.Size = ARM_MPU_REGION_SIZE_64KB;
MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS;
MPU_InitStruct.IsBufferable = MPU_ACCESS_BUFFERABLE;
MPU_InitStruct.IsCacheable = MPU_ACCESS_NOT_CACHEABLE;
MPU_InitStruct.IsShareable = MPU_ACCESS_NOT_SHAREABLE;
MPU_InitStruct.Number = MPU_REGION_NUMBER1;
MPU_InitStruct.TypeExtField = MPU_TEX_LEVEL0;
MPU_InitStruct.SubRegionDisable = 0x00;
MPU_InitStruct.DisableExec = MPU_INSTRUCTION_ACCESS_ENABLE;
HAL_MPU_ConfigRegion(&MPU_InitStruct);
/*使能 MPU */
HAL_MPU_Enable(MPU_PRIVILEGED_DEFAULT);
}
/*
*********************************************************************************************************
* 函 数 名: CPU_CACHE_Enable
* 功能说明: 使能L1 Cache
* 形 参: 无
* 返 回 值: 无
*********************************************************************************************************
*/
static void CPU_CACHE_Enable(void)
{
/* 使能 I-Cache */
SCB_EnableICache();
/* 使能 D-Cache */
SCB_EnableDCache();
}
每10ms调用一次按键处理:
按键处理是在滴答定时器中断里面实现,每10ms执行一次检测。
/*
*********************************************************************************************************
* 函 数 名: bsp_RunPer10ms
* 功能说明: 该函数每隔10ms被Systick中断调用1次。详见 bsp_timer.c的定时中断服务程序。一些处理时间要求
* 不严格的任务可以放在此函数。比如:按键扫描、蜂鸣器鸣叫控制等。
* 形 参: 无
* 返 回 值: 无
*********************************************************************************************************
*/
void bsp_RunPer10ms(void)
{
bsp_KeyScan10ms();
}
主功能:
主程序实现如下操作:
/*
*********************************************************************************************************
* 函 数 名: main
* 功能说明: c程序入口
* 形 参: 无
* 返 回 值: 错误代码(无需处理)
*********************************************************************************************************
*/
int main(void)
{
uint8_t i;
int32_t iTemp;
float fTemp;
bsp_Init(); /* 硬件初始化 */
PrintfLogo(); /* 打印例程信息到串口1 */
PrintfHelp(); /* 打印操作提示信息 */
bsp_DelayMS(500); /* 等待上电稳定,等基准电压电路稳定, bsp_InitADS1256() 内部会进行自校准 */
bsp_InitADS1256(); /* 初始化配置ADS1256. PGA=1, DRATE=30KSPS, BUFEN=1, 输入正负5V */
/* 打印芯片ID (通过读ID可以判断硬件接口是否正常) , 正常时状态寄存器的高4bit = 3 */
#if 0
{
uint8_t id;
id = ADS1256_ReadChipID();
if (id != 3)
{
printf("Error, ASD1256 Chip ID = 0x%X\r\n", id);
}
else
{
printf("Ok, ASD1256 Chip ID = 0x%X\r\n", id);
}
}
#endif
ADS1256_CfgADC(ADS1256_GAIN_1, ADS1256_30SPS); /* 配置ADC参数: 增益1:1, 数据输出速率 30Hz */
/* 启动中断扫描模式, 轮流采集8个通道的ADC数据. 通过 ADS1256_GetAdc() 函数来读取这些数据 */
ADS1256_StartScan();
bsp_StartAutoTimer(0, 1000); /* 启动1个100ms的自动重装的定时器 */
/* 进入主程序循环体 */
while (1)
{
bsp_Idle(); /* 这个函数在bsp.c文件。用户可以修改这个函数实现CPU休眠和喂狗 */
if (bsp_CheckTimer(0)) /* 判断定时器超时时间 */
{
/* 每隔1000ms 进来一次 */
bsp_LedToggle(2); /* 翻转LED的状态 */
/* 打印采集数据 */
for (i = 0; i < 8; i++)
{
/*
计算公式:2 * VREF/(PGA * (2^23 - 1)) ,这里VREF是2.5V,PGA = 1
*/
/* 计算实际电压值(近似估算的),如需准确,请进行校准 */
iTemp = ((int64_t)g_tADS1256.AdcNow[i] * 2500000) / 4194303;
fTemp = (float)iTemp / 1000000;
printf("CH%d=%07d(%fV) ", i, g_tADS1256.AdcNow[i], fTemp);
if(i == 3)
{
printf("\r\n");
}
}
printf("\r\n\r\n");
}
}
}
配套例子:
V7-068_ADS1256(8通道带PGA的24位ADC)
实验目的:
重要提示:
实验内容:
上电后串口打印的信息:
波特率 115200,数据位 8,奇偶校验位无,停止位 1。
模块插入位置:
程序设计:
系统栈大小分配:
RAM空间用的DTCM:
硬件外设初始化
硬件外设的初始化是在 bsp.c 文件实现:
/*
*********************************************************************************************************
* 函 数 名: bsp_Init
* 功能说明: 初始化所有的硬件设备。该函数配置CPU寄存器和外设的寄存器并初始化一些全局变量。只需要调用一次
* 形 参:无
* 返 回 值: 无
*********************************************************************************************************
*/
void bsp_Init(void)
{
/* 配置MPU */
MPU_Config();
/* 使能L1 Cache */
CPU_CACHE_Enable();
/*
STM32H7xx HAL 库初始化,此时系统用的还是H7自带的64MHz,HSI时钟:
- 调用函数HAL_InitTick,初始化滴答时钟中断1ms。
- 设置NVIV优先级分组为4。
*/
HAL_Init();
/*
配置系统时钟到400MHz
- 切换使用HSE。
- 此函数会更新全局变量SystemCoreClock,并重新配置HAL_InitTick。
*/
SystemClock_Config();
/*
Event Recorder:
- 可用于代码执行时间测量,MDK5.25及其以上版本才支持,IAR不支持。
- 默认不开启,如果要使能此选项,务必看V7开发板用户手册第xx章
*/
#if Enable_EventRecorder == 1
/* 初始化EventRecorder并开启 */
EventRecorderInitialize(EventRecordAll, 1U);
EventRecorderStart();
#endif
bsp_InitDWT(); /* 初始化DWT时钟周期计数器 */
bsp_InitKey(); /* 按键初始化,要放在滴答定时器之前,因为按钮检测是通过滴答定时器扫描 */
bsp_InitTimer(); /* 初始化滴答定时器 */
bsp_InitLPUart(); /* 初始化串口 */
bsp_InitExtIO(); /* 初始化FMC总线74HC574扩展IO. 必须在 bsp_InitLed()前执行 */
bsp_InitLed(); /* 初始化LED */
bsp_InitExtSDRAM(); /* 初始化SDRAM */
/* 针对不同的应用程序,添加需要的底层驱动模块初始化函数 */
bsp_InitAD7606(); /* 配置AD7606所用的GPIO */
}
MPU配置和Cache配置:
数据Cache和指令Cache都开启。配置了AXI SRAM区(本例子未用到AXI SRAM)和FMC的扩展IO区。
/*
*********************************************************************************************************
* 函 数 名: MPU_Config
* 功能说明: 配置MPU
* 形 参: 无
* 返 回 值: 无
*********************************************************************************************************
*/
static void MPU_Config( void )
{
MPU_Region_InitTypeDef MPU_InitStruct;
/* 禁止 MPU */
HAL_MPU_Disable();
/* 配置AXI SRAM的MPU属性为Write back, Read allocate,Write allocate */
MPU_InitStruct.Enable = MPU_REGION_ENABLE;
MPU_InitStruct.BaseAddress = 0x24000000;
MPU_InitStruct.Size = MPU_REGION_SIZE_512KB;
MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS;
MPU_InitStruct.IsBufferable = MPU_ACCESS_BUFFERABLE;
MPU_InitStruct.IsCacheable = MPU_ACCESS_CACHEABLE;
MPU_InitStruct.IsShareable = MPU_ACCESS_NOT_SHAREABLE;
MPU_InitStruct.Number = MPU_REGION_NUMBER0;
MPU_InitStruct.TypeExtField = MPU_TEX_LEVEL1;
MPU_InitStruct.SubRegionDisable = 0x00;
MPU_InitStruct.DisableExec = MPU_INSTRUCTION_ACCESS_ENABLE;
HAL_MPU_ConfigRegion(&MPU_InitStruct);
/* 配置FMC扩展IO的MPU属性为Device或者Strongly Ordered */
MPU_InitStruct.Enable = MPU_REGION_ENABLE;
MPU_InitStruct.BaseAddress = 0x60000000;
MPU_InitStruct.Size = ARM_MPU_REGION_SIZE_64KB;
MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS;
MPU_InitStruct.IsBufferable = MPU_ACCESS_BUFFERABLE;
MPU_InitStruct.IsCacheable = MPU_ACCESS_NOT_CACHEABLE;
MPU_InitStruct.IsShareable = MPU_ACCESS_NOT_SHAREABLE;
MPU_InitStruct.Number = MPU_REGION_NUMBER1;
MPU_InitStruct.TypeExtField = MPU_TEX_LEVEL0;
MPU_InitStruct.SubRegionDisable = 0x00;
MPU_InitStruct.DisableExec = MPU_INSTRUCTION_ACCESS_ENABLE;
HAL_MPU_ConfigRegion(&MPU_InitStruct);
/*使能 MPU */
HAL_MPU_Enable(MPU_PRIVILEGED_DEFAULT);
}
/*
*********************************************************************************************************
* 函 数 名: CPU_CACHE_Enable
* 功能说明: 使能L1 Cache
* 形 参: 无
* 返 回 值: 无
*********************************************************************************************************
*/
static void CPU_CACHE_Enable(void)
{
/* 使能 I-Cache */
SCB_EnableICache();
/* 使能 D-Cache */
SCB_EnableDCache();
}
每10ms调用一次按键处理:
按键处理是在滴答定时器中断里面实现,每10ms执行一次检测。
/*
*********************************************************************************************************
* 函 数 名: bsp_RunPer10ms
* 功能说明: 该函数每隔10ms被Systick中断调用1次。详见 bsp_timer.c的定时中断服务程序。一些处理时间要求
* 不严格的任务可以放在此函数。比如:按键扫描、蜂鸣器鸣叫控制等。
* 形 参: 无
* 返 回 值: 无
*********************************************************************************************************
*/
void bsp_RunPer10ms(void)
{
bsp_KeyScan10ms();
}
主功能:
主程序实现如下操作:
/*
*********************************************************************************************************
* 函 数 名: main
* 功能说明: c程序入口
* 形 参: 无
* 返 回 值: 错误代码(无需处理)
*********************************************************************************************************
*/
int main(void)
{
uint8_t i;
int32_t iTemp;
float fTemp;
bsp_Init(); /* 硬件初始化 */
PrintfLogo(); /* 打印例程信息到串口1 */
PrintfHelp(); /* 打印操作提示信息 */
bsp_DelayMS(500); /* 等待上电稳定,等基准电压电路稳定, bsp_InitADS1256() 内部会进行自校准 */
bsp_InitADS1256(); /* 初始化配置ADS1256. PGA=1, DRATE=30KSPS, BUFEN=1, 输入正负5V */
/* 打印芯片ID (通过读ID可以判断硬件接口是否正常) , 正常时状态寄存器的高4bit = 3 */
#if 0
{
uint8_t id;
id = ADS1256_ReadChipID();
if (id != 3)
{
printf("Error, ASD1256 Chip ID = 0x%X\r\n", id);
}
else
{
printf("Ok, ASD1256 Chip ID = 0x%X\r\n", id);
}
}
#endif
ADS1256_CfgADC(ADS1256_GAIN_1, ADS1256_30SPS); /* 配置ADC参数: 增益1:1, 数据输出速率 30Hz */
/* 启动中断扫描模式, 轮流采集8个通道的ADC数据. 通过 ADS1256_GetAdc() 函数来读取这些数据 */
ADS1256_StartScan();
bsp_StartAutoTimer(0, 1000); /* 启动1个100ms的自动重装的定时器 */
/* 进入主程序循环体 */
while (1)
{
bsp_Idle(); /* 这个函数在bsp.c文件。用户可以修改这个函数实现CPU休眠和喂狗 */
if (bsp_CheckTimer(0)) /* 判断定时器超时时间 */
{
/* 每隔1000ms 进来一次 */
bsp_LedToggle(2); /* 翻转LED的状态 */
/* 打印采集数据 */
for (i = 0; i < 8; i++)
{
/*
计算公式:2 * VREF/(PGA * (2^23 - 1)) ,这里VREF是2.5V,PGA = 1
*/
/* 计算实际电压值(近似估算的),如需准确,请进行校准 */
iTemp = ((int64_t)g_tADS1256.AdcNow[i] * 2500000) / 4194303;
fTemp = (float)iTemp / 1000000;
printf("CH%d=%07d(%fV) ", i, g_tADS1256.AdcNow[i], fTemp);
if(i == 3)
{
printf("\r\n");
}
}
printf("\r\n\r\n");
}
}
}
本章节涉及到的知识点非常多,主要为大家讲解了ADS1256的常用玩法,如果实际项目中用到此芯片需要熟练运用。