STM32驱动AD7366-5/AD7367-5芯片

news/2024/11/19 6:37:12/

目录

芯片介绍

芯片引脚定义

芯片电路图

根据时序写驱动代码

运行环境

时序与代码讲解

 完整的代码


在实验室项目上,根据需求最终选用到了这款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,23DoutA,DoutB串行数据输出引脚。AD7366需要提供12个SCLK,AD7367需要提供14个SCLK,且在下降沿的时候读取数据。
2V drive逻辑电源输入。
3DVcc数字电源电压。这里最好和AVcc短接
4,5RANGE1、RANGE0 模拟输入范围选择。 这些引脚上的逻辑电平决定模拟输入通道的输入范围。详情见下表
6ADDR模拟输入通道选择。详情见下表
7,17AGND模拟地
8AVcc模拟电压,4.75V-5.25V
9,16DcapA,DcapB电容引脚,接680nF
10Vss负电源引脚
11,12VA1、VA2

模拟转换器A的两个输入引脚

13,14VB1、VB2

模拟转换器B的两个输入引脚

15VDD正电源引脚
18REFSEL参考电压选择。逻辑高电平:选择片内参考电压;逻辑低电平:通过DcapA,DcapB输入参考电压。
19CSDoutA,DoutB数据输出使能端。低电平有效
20SCLK串行时钟信号
21CNVST数据转换启动引脚。下降沿启动。
22BUSY‘忙’信号输出。数据正在转换时,该引脚输出高
24DGND数字地

模拟输入范围的供应要求参考

模拟输入范围的参考 (RANGE1、RANGE0)

通道选择引脚(ADDR)

 

芯片电路图

根据时序写驱动代码

运行环境

芯片:STM32RCT6

编译工具:RT Thread studio (1.0.6)

时序与代码讲解

 

这里官方给的时序图已经很清晰了。具体步骤如下:

  1. 在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引脚给高
    }

     

  2. 在第一个步骤之后,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);        //中断使能
  3. 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);}

     

  4. 由于我设置的输入电压范围为+-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_ */

 


http://www.ppmy.cn/news/404552.html

相关文章

一、正确安装 Keras TensorFlow 并调用 TensorBoard 绘制训练曲线

本博客主要内容为使用Keras实例化深度学习模型&#xff0c;并对在这个过程中遇到的问题进行总结&#xff0c;主要参考《Keras中文文档》&#xff0c;在配置环境、安装软件以及调试的过程中遇到的各种问题主要参考各个网页&#xff0c;这些网页会在必要时给出参考。 如本篇博客没…

ac3165无线网卡驱动linux,英特尔ac3165驱动下载

ac3165网卡驱动是支持英特尔双频带Wireless-AC3165无线网卡的安装程序&#xff0c;让用户的台式电脑也可以接受wifi&#xff0c;并且不卡顿的玩游戏和浏览网页&#xff0c;低延迟、高速率&#xff0c;同插网线一样的网速效果&#xff0c;有该产品的可以试试&#xff0c;安装傻瓜…

关于GTPU

GTP协议 GTP&#xff08;GPRS隧道协议(GPRS Tunneling Protocol)&#xff09;&#xff0c;可以分解成三种独立的协议&#xff0c;GTP-C、GTP-U及GTP。GTP-U用于在GPRS核心网内&#xff0c;无线接入网与核心网之间传送用户数据。 GTP可以用在UDP或TCP上&#xff0c;GTP v1仅用于…

python-文件操作指针

文件操作指针 文章目录 文件操作指针1. 文件指针的概念和作用&#xff1a;2. 移动文件指针的位置&#xff1a;3. 获取当前文件指针的位置&#xff1a;4. 文件指针的影响&#xff1a; 文件操作指针是在进行文件读写操作时用于标识当前位置的一个概念。它记录了文件中读写操作将要…

【OpenMMLab AI实战营二期笔记】第十天 底层视觉与MMEditing

1.图像超分辨率 1.1 什么是图像超分辨率&#xff1f; 根据从低分辨率图像重构高分辨率图像 1.2 目标&#xff1a; 提高图像的分辨率高分图像符合低分图像的内容恢复图像的细节、产生真实的内容 1.3 应用&#xff1a; 经典游戏高清重制动画高清重制照片修复节约传输高清图…

LuaJIT Bytecode结构布局

一、Bytecode的存储结构 LuaJIT的Bytecode位宽为32位&#xff0c;在parse阶段用结构体BCInsLine表示&#xff0c;ins表示32位长的字字节码指令&#xff0c;line表示字节码的行号&#xff1a; typedef struct BCInsLine {BCIns ins; /* Bytecode instruction. */BC…

JUC高级-0614

5.LockSupport与线程中断 5.1 线程中断 蚂蚁金服面试题&#xff1a;如何中等一个线程&#xff0c;如何停止一个线程什么是中断机制 首先&#xff1a;一个线程不应该由其他线程来强制中断或停止&#xff0c;而是应该由线程自己自行停止。所以&#xff0c;Thread.stop, Thread.…

el-table点击单元格变成输入框,以及其自动获取焦点失效可能的原因(focus失效)

1.el-table点击单元格变成输入框 这里主要使用了el-table三个自带的方法/属性&#xff1a; <el-table:data"MesTableData"bordercell-click"clickCell":row-class-name"tableRowClassName":cell-class-name"tableCellClassName" …