目录
芯片介绍
芯片引脚定义
芯片电路图
根据时序写驱动代码
运行环境
时序与代码讲解
完整的代码
在实验室项目上,根据需求最终选用到了这款AD7367-5,在此记录一下这款AD芯片的驱动调试过程。目前仅用到了AD7367-5的第一组通道(A1和B1),后续若有其他要求会持续更新。(AD7366-5的驱动过程和这个差不多,只是数据位不同而已)
芯片介绍
AD7366/AD7367是一款2通道的12位/14位、高速、低功耗、单双极性转换的高精度串行逐次逼近型ADC,采样速率最高可达1MS/s。在连续采样的情况下。完成一次转换的时间,包括数据读取不会超过1us。AD7366/AD7367还具有可编程选择模拟输入电压范围的功能,具有三种不同的输入范围:+-10v、+-5v以及0~10v,AD7366和AD7367的转换过程和控制时序都是一致的,唯一不同的是,AD7366是12位的串行ADC,AD7367是14位的串行ADC,也就是说他们的转换结果位数不同。在本文,我仅以AD7367-5为例来进行说明。
芯片引脚定义
引脚编号 | 标号 | 说明 |
1,23 | DoutA,DoutB | 串行数据输出引脚。AD7366需要提供12个SCLK,AD7367需要提供14个SCLK,且在下降沿的时候读取数据。 |
2 | V drive | 逻辑电源输入。 |
3 | DVcc | 数字电源电压。这里最好和AVcc短接 |
4,5 | RANGE1、RANGE0 | 模拟输入范围选择。 这些引脚上的逻辑电平决定模拟输入通道的输入范围。详情见下表 |
6 | ADDR | 模拟输入通道选择。详情见下表 |
7,17 | AGND | 模拟地 |
8 | AVcc | 模拟电压,4.75V-5.25V |
9,16 | DcapA,DcapB | 电容引脚,接680nF |
10 | Vss | 负电源引脚 |
11,12 | VA1、VA2 | 模拟转换器A的两个输入引脚 |
13,14 | VB1、VB2 | 模拟转换器B的两个输入引脚 |
15 | VDD | 正电源引脚 |
18 | REFSEL | 参考电压选择。逻辑高电平:选择片内参考电压;逻辑低电平:通过DcapA,DcapB输入参考电压。 |
19 | CS | DoutA,DoutB数据输出使能端。低电平有效 |
20 | SCLK | 串行时钟信号 |
21 | CNVST | 数据转换启动引脚。下降沿启动。 |
22 | BUSY | ‘忙’信号输出。数据正在转换时,该引脚输出高 |
24 | DGND | 数字地 |
模拟输入范围的供应要求参考
模拟输入范围的参考 (RANGE1、RANGE0)
通道选择引脚(ADDR)
芯片电路图
根据时序写驱动代码
运行环境
芯片:STM32RCT6
编译工具:RT Thread studio (1.0.6)
时序与代码讲解
这里官方给的时序图已经很清晰了。具体步骤如下:
- 在CNVST引脚输入一个下降沿信号,告诉AD7367现在立刻启动转换器,这里CNVST低电平持续的最低时间是10ns;
/** 函数名称:ADC7367_conversion* 函数功能:开启一次数据的转换* 函数返回:* */ static void ADC7367_conversion() {rt_pin_write(ADC7367_CNVST_PIN, PIN_LOW); //CNVST引脚给低rt_hw_us_delay(1); //延时1usrt_pin_write(ADC7367_CNVST_PIN, PIN_HIGH); //CNVST引脚给高 }
- 在第一个步骤之后,AD7367就开始转换数据了,AD7367转换数据的时间最多为1.25us,完成之后输出一个下降沿信号,这就需要捕获这个下降沿信号,故设置引脚为输入模式,并配置为下降沿触发中断;
rt_pin_mode(ADC7367_BUSY_PIN, PIN_MODE_INPUT_PULLDOWN); //下拉输入rt_pin_attach_irq(ADC7367_BUSY_PIN, PIN_IRQ_MODE_FALLING, ADC7367_BUSY_Rt, RT_NULL); //配置Busy引脚中断(下降沿触发) ADC7367_BUSY_Rt为中断服务函数rt_pin_irq_enable(ADC7367_BUSY_PIN, PIN_IRQ_ENABLE); //中断使能
- STM32捕获到AD7367--BUSY引脚输出的下降沿信号之后,就开始读取转换后的AD数据。需要注意的是:(花了很长找到的Bug,主要是少看了一句)The falling edge of CS takes the bus out of three state and clocks out the MSB of the conversion result.,这里的意思是:在给CS引脚输入一个下降沿的时候,AD就已经输出第一个数据了。得到这第一个数据之后,在SCLK引脚给13个周期信号(周期最小为:10k/0.6HZ,最大为:20M/0.6HZ)获取剩下的数据,共14位。
/** 函数名称:ADC7367_read_data* 函数功能:读一次AD的14位数据* */ static void ADC7367_read_data() {AD_data_A=0;AD_data_B=0;rt_pin_write(ADC7367_CLK_PIN, PIN_HIGH);rt_pin_write(ADC7367_CS_PIN, PIN_LOW);if(rt_pin_read(ADC7367_OUTA_PIN)) AD_data_A|=1<<13;//获取最高位if(rt_pin_read(ADC7367_OUTB_PIN)) AD_data_B|=1<<13;rt_hw_us_delay(1);for(int8_t i=12;i>=0;i--){rt_pin_write(ADC7367_CLK_PIN, PIN_LOW);if(rt_pin_read(ADC7367_OUTA_PIN)) AD_data_A|=1<<i;if(rt_pin_read(ADC7367_OUTB_PIN)) AD_data_B|=1<<i;rt_hw_us_delay(1);rt_pin_write(ADC7367_CLK_PIN, PIN_HIGH);rt_hw_us_delay(1);}rt_pin_write(ADC7367_CS_PIN, PIN_HIGH);}
-
由于我设置的输入电压范围为+-10v,因此在得到的14位数据中,最高位为符号位,具体看下表。首先就要判断数据正负,如果最高位为1(表示负),就需要取后面13位数据的补码,再进行换算;最高位为0的话(表示正),后面13位数据就不需要再转换了。根据实际测的情况,如果安装官方给的LSB size进行电压转换的话,精确度不高,所以我就自己算出了一个LSB值为:1.21420087mv.
if(AD_data_A&0x2000) //判断正负电压
{AD_A_data_Int=((~AD_data_A)&0x1FFF)*AD_k; //负数就取补码LOG_I("Data A(V): -%d V\n",AD_A_data_Int);
}else {AD_A_data_Int=(AD_data_A&0x1FFF)*AD_k;LOG_I("Data A(V): +%d V\n",AD_A_data_Int);
}
完整的代码
这里我加了一个定时器用于判断AD是否转换错误,或者GG了。
ADC7367.C文件
/** Copyright (c) 2006-2020, RT-Thread Development Team** SPDX-License-Identifier: Apache-2.0** Change Logs:* Date Author Notes* 2020-05-05 LD the first version*/
#include "ADC7367.h"#define Conversion_out 0x03 //转换超时
#define Conversion_now 0x02 //正在转换
#define Conversion_free 0x01 //空闲状态
#define Conversion_end 0x04 //转换结束
#define AD_k 1.21420087 //AD转换系数rt_thread_t thread_AD;uint8_t Conversion_sta=Conversion_free; //AD数据转换初始状态
uint16_t AD_data_A; //数据A
uint16_t AD_data_B; //数据B
uint16_t AD_data_A_last; //数据A上一个值
uint16_t AD_data_B_last; //数据B上一个值extern int time1_outnum; //定义超时的次数
extern rt_timer_t timer1;/** 函数名称:ADC7367_BUSY_Rt* 函数功能:busy信号下降沿中断函数* */
static void ADC7367_BUSY_Rt()
{Conversion_sta=Conversion_end;//数据转换结束}/** 函数名称:ADC7367_conversion* 函数功能:开启一次数据的转换* 函数返回:* */
static void ADC7367_conversion()
{rt_pin_write(ADC7367_CNVST_PIN, PIN_LOW); //CNVST引脚给低rt_hw_us_delay(1); //延时1usrt_pin_write(ADC7367_CNVST_PIN, PIN_HIGH); //CNVST引脚给高
}
/** 函数名称:ADC7367_read_data* 函数功能:读一次AD的14位数据* */
static void ADC7367_read_data()
{AD_data_A=0;AD_data_B=0;rt_pin_write(ADC7367_CLK_PIN, PIN_HIGH);rt_pin_write(ADC7367_CS_PIN, PIN_LOW);if(rt_pin_read(ADC7367_OUTA_PIN)) AD_data_A|=1<<13;//获取最高位if(rt_pin_read(ADC7367_OUTB_PIN)) AD_data_B|=1<<13;rt_hw_us_delay(1);for(int8_t i=12;i>=0;i--){rt_pin_write(ADC7367_CLK_PIN, PIN_LOW);if(rt_pin_read(ADC7367_OUTA_PIN)) AD_data_A|=1<<i;if(rt_pin_read(ADC7367_OUTB_PIN)) AD_data_B|=1<<i;rt_hw_us_delay(1);rt_pin_write(ADC7367_CLK_PIN, PIN_HIGH);rt_hw_us_delay(1);}rt_pin_write(ADC7367_CS_PIN, PIN_HIGH);}/** 函数名称:ADC7367_thread_entry (!!!!!Main函数!!!!!)* 函数功能:ADC7367线程* */
static void ADC7367_thread_entry(void *parameter)
{int AD_A_data_Int=0;rt_pin_mode(ADC7367_OUTA_PIN, PIN_MODE_INPUT_PULLDOWN); //下拉输入rt_pin_mode(ADC7367_OUTB_PIN, PIN_MODE_INPUT_PULLDOWN);rt_pin_mode(ADC7367_BUSY_PIN, PIN_MODE_INPUT_PULLDOWN);rt_pin_mode(ADC7367_CS_PIN, PIN_MODE_OUTPUT); //推挽输出rt_pin_mode(ADC7367_CLK_PIN, PIN_MODE_OUTPUT);rt_pin_mode(ADC7367_R0_PIN, PIN_MODE_OUTPUT);rt_pin_mode(ADC7367_R1_PIN, PIN_MODE_OUTPUT);rt_pin_mode(ADC7367_CNVST_PIN, PIN_MODE_OUTPUT);rt_pin_mode(ADC7367_ADDR_PIN, PIN_MODE_OUTPUT);rt_pin_write(ADC7367_CNVST_PIN, PIN_HIGH);rt_pin_write(ADC7367_CLK_PIN, PIN_HIGH);rt_pin_write(ADC7367_CS_PIN, PIN_HIGH);rt_pin_write(ADC7367_R0_PIN, PIN_LOW); //模拟输入范围选择+-10Vrt_pin_write(ADC7367_R1_PIN, PIN_LOW);rt_pin_write(ADC7367_ADDR_PIN, PIN_LOW); //选择通道A1和B1rt_pin_attach_irq(ADC7367_BUSY_PIN, PIN_IRQ_MODE_FALLING, ADC7367_BUSY_Rt, RT_NULL); // Busy引脚中断(下降沿触发)rt_pin_irq_enable(ADC7367_BUSY_PIN, PIN_IRQ_ENABLE);rt_thread_mdelay(100);while (1){switch (Conversion_sta) {case Conversion_free:rt_timer_start(timer1); //重启定时器LOG_I("ADC start conversion!\n");ADC7367_conversion(); //转换一次数据break;case Conversion_out:LOG_W("ADC conversion out time!,number:%d\n",time1_outnum);Conversion_sta=Conversion_free;break;case Conversion_end: //数据转换结束,开始读数据LOG_I("ADC conversion over!\n");ADC7367_read_data();if(AD_data_A&0x2000) //判断正负电压{AD_A_data_Int=((~AD_data_A)&0x1FFF)*AD_k; //负数就取补码LOG_I("Data A(V): -%d V\n",AD_A_data_Int);}else {AD_A_data_Int=(AD_data_A&0x1FFF)*AD_k;LOG_I("Data A(V): +%d V\n",AD_A_data_Int);}// LOG_I("Data A(V): %04x V\n",AD_data_A);
// LOG_I("Data B(I): %04x A\n",AD_data_B);time1_outnum=0; //定时器 超时次数清0 Conversion_sta=Conversion_free;rt_thread_mdelay(1000);break;default:break;}rt_hw_us_delay(1);}
}static int ADC7367_init()
{rt_err_t ret = RT_EOK;/* 创建 serial 动态线程 */thread_AD = rt_thread_create("serial_ADC7367", ADC7367_thread_entry, RT_NULL,ADC7367_stack_size,ADC7367_priority,ADC7367_tick);/* 创建成功则启动线程 */if (thread_AD != RT_NULL){rt_thread_startup(thread_AD);}else{ret = RT_ERROR;}return ret;
}/* 加入到 初始化 列表中 */
INIT_APP_EXPORT(ADC7367_init);
/* 导出到 msh 命令列表中 */
MSH_CMD_EXPORT(ADC7367_init, ADC7367_init device Init function);
ADC7367.h文件
/** Copyright (c) 2006-2020, RT-Thread Development Team** SPDX-License-Identifier: Apache-2.0** Change Logs:* Date Author Notes* 2020-05-05 LD the first version*/
#ifndef APPLICATIONS_ADC7367_ADC7367_H_
#define APPLICATIONS_ADC7367_ADC7367_H_#include <rtthread.h>
#include <board.h>
#include <rtdevice.h>
#include "string.h"
#include "mb.h"
#include "user_mb_app.h"#define DBG_TAG "ADC7367"
#define DBG_LVL DBG_LOG
#include <rtdbg.h>/********************线程配置宏定义***********************/
#define ADC7367_stack_size 1024
#define ADC7367_priority 13
#define ADC7367_tick 10/********************ADC7367引脚宏定义***********************/
#define ADC7367_OUTA_PIN GET_PIN(B, 12)
#define ADC7367_OUTB_PIN GET_PIN(C, 6)
#define ADC7367_CS_PIN GET_PIN(A, 8)
#define ADC7367_R0_PIN GET_PIN(B, 14)
#define ADC7367_R1_PIN GET_PIN(B, 13)
#define ADC7367_CLK_PIN GET_PIN(C, 9)
#define ADC7367_CNVST_PIN GET_PIN(C, 8)
#define ADC7367_BUSY_PIN GET_PIN(C, 7)
#define ADC7367_ADDR_PIN GET_PIN(B, 15)/********************定时器宏定义***********************/
#define HWTIMER_DEV_NAME "timer2" /* 定时器名称 *//********************Modbus寄存器***********************/
extern USHORT usSRegHoldBuf[S_REG_HOLDING_NREGS];#endif /* APPLICATIONS_ADC7367_ADC7367_H_ */