将BURST
引脚置
1
,就会在硬件模式下使能突发模式。还要将SEQEN
引脚置
1
以使能序列器。
在硬件模式下,突发序列器由BURST
、
SEQEN
和
CHSELx
引脚配置。当
AD7616
退出完全复位时,突发序列器要么使能,要么禁用。当释放RESET
引脚时,
SEQEN
引脚和
BURST
引脚的逻辑电平决定突发序列器是使能还是禁用。释放RESET
引脚后,该功能便固定下来,要退出该功能并设置另一种配置,需要通过RESET
引脚执行完全复位。
当突发序列器使能时,CHSELx
引脚的逻辑电平决定选择哪些通道在突发序列中进行转换。释放RESET
引脚时的
CHSELx
引脚状态决定要在序列中转换的通道初始设置。要在复位后重新
配置选定进行转换的通道,请将
CHSELx
引脚设为所需的设置并保持下一个BUSY
脉冲时间。
3. 驱动代码编写
3.1 配置引脚功能
配置AD7616引脚功能时需要注意,BUSY引脚类型为DO(只具备输出功能),而对于主芯片(GD32)来说,只需要配置为输入模式即可。而AD7616数据总线DB0-DB15为数字输出,对于主芯片而言并非模拟输入,而是数字输入(因为AD7616通过模拟输入引脚采集模拟数据,芯片内部已经将模拟量转换为数字量)。
//GPIO引脚初始化
void AD7616_PinInit(void)
{
gpio_init(GPIOE,GPIO_MODE_OUT_PP,GPIO_OSPEED_50MHZ,GPIO_PIN_9|\
GPIO_PIN_10|GPIO_PIN_11);//通用推挽输出
gpio_init(GPIOE,GPIO_MODE_IN_FLOATING,GPIO_OSPEED_50MHZ,GPIO_PIN_12);//浮空输入
gpio_init(GPIOB,GPIO_MODE_OUT_PP,GPIO_OSPEED_50MHZ,GPIO_PIN_11);//通用推挽输出
gpio_init(GPIOD,GPIO_MODE_IPU|GPIO_MODE_IPD,GPIO_OSPEED_50MHZ,GPIO_PIN_ALL);//PD所有引脚为上下拉输入
}
//配置BUSY引脚外部中断
void AD7616_BUSY_ExtiConfig(void)
{
gpio_exti_source_select(GPIO_PORT_SOURCE_GPIOE,GPIO_PIN_SOURCE_12);//选择gpio引脚中断源
exti_init(EXTI_12,EXTI_INTERRUPT,EXTI_TRIG_FALLING);//下降沿触发
exti_interrupt_flag_clear(EXTI_12);//清除中断标志位
exti_interrupt_enable(EXTI_12);//启用中断线
}
3.2 AD7616初始化配置
本文使用的主芯片(GD32F10x)时钟频率为108MHZ,
而延时函数AD7616_Delay_9ns(uint32_t ns)只是通过while循环做空操作。
/*
\brief: AD7616复位功能配置
\param: mode : 复位模式
AD7616_ALL_RESER :完全复位
AD7616_PART_RESER :部分复位
\retval: none
*/
void AD7616_Reser(uint8_t mode)
{
switch(mode)
{
case AD7616_ALL_RESER: //完全复位
AD7616_RESET_0;
AD7616_Delay_9ns(140);
AD7616_RESET_1;
delay_1ms(15);
break;
case AD7616_PART_RESER: //部分复位
AD7616_RESET_0;
delay_1us(40);
AD7616_RESET_1;
AD7616_Delay_9ns(5);
break;
default:
break;
}
}
//AD7616初始化
void AD7616_Init(void)
{
AD7616_PinInit();//引脚初始化
AD7616_BUSY_ExtiConfig();//配置BUSY引脚外部中断
//AD7616_RESET_1;
//AD7616_Reser(AD7616_PART_RESER);//部分复位
AD7616_Reser(AD7616_ALL_RESER);//完全复位
//未使能输出总线
AD7616_RD_1;
AD7616_CS_1;
AD7616_CONVST_0;//拉低CONVST电平
}
3.3启动一次转换
//AD值转换为实际温度值
float AD7616_ad_TO_temp(uint16_t i,const uint16_t ad)
{
return (float)(AD7616_CH_member[i].A*ad*ad + AD7616_CH_member[i].B*ad + AD7616_CH_member[i].C);
}
/*
AD7616转换函数
*/
void EXTI10_15_IRQHandler(void)
{
if(RESET!=exti_interrupt_flag_get(EXTI_12))
{
exti_interrupt_flag_clear(EXTI_12);//清除中断标志位
uint16_t i;
AD7616_CONVST_0;
for(i=0;i<AD7616_CHANNEL_VAL;i++)
{
//printf("BUSY=%d\n",gpio_input_bit_get(GPIOE,GPIO_PIN_12));//启动转换后BUSY为低电平
AD7616_CS_0;
AD7616_RD_0; //低电平时间>30ns
AD7616_Data[i]=AD7616_GET_DATA();//读取数据
AD7616_RD_1; //高电平时间>10ns
AD7616_CS_1;
AD7616_Delay_9ns(3);
}
AD7616_READ_STATE=AD7616_STATE_READABLE;//可读
//AD7616_CONVST_0;
}
}
//启动转换
void AD7616_StartConvst(void)
{
memset(AD7616_Data,0,AD7616_CHANNEL_VAL);//清空缓存
//AD7616_CONVST_0;
//AD7616_Delay_9ns(1);
AD7616_CONVST_1;
//printf("BUSY=%d\n",gpio_input_bit_get(GPIOE,GPIO_PIN_12));//未启动转换时BUSY为高电平
}
/*
\brief: 启动多次转换,并求取各通道AD平均值
\param: *ad 接收AD值的地址
count 启动次数
\retval: ERROR 超时 SUCCESS 成功转换
*/
ErrStatus AD7616_Convst_Average(const uint32_t count)
{
uint32_t i,j;
uint32_t sum[AD7616_CHANNEL_VAL]={0};
for(i=0;i<count;i++)
{
AD7616_READ_STATE=AD7616_STATE_UNREADABLE;//不可读
AD7616_StartConvst();//开始转换
j=0;
while(AD7616_READ_STATE!=AD7616_STATE_READABLE)//等待转换完成
{
AD7616_Delay_9ns(1);
j++;
if(j==500000) return ERROR;//超时
}
for(j=0;j<AD7616_CHANNEL_VAL;j++)
{
sum[j]+=AD7616_Data[j];
}
}
//取平均值
for(i=0;i<AD7616_CHANNEL_VAL;i++)
{
AD7616_AD[i]=sum[i]/count;
AD7616_Temp[i]=AD7616_ad_TO_temp(i,AD7616_AD[i]);//转换温度
}
return SUCCESS;
}
3.4 头文件
#ifndef _AD7616_H_
#define _AD7616_H_
#include "gd32f10x.h"
#include "flash.h"
#include <string.h>
#include <stdlib.h>
/*
Pin:
RD -- PE9
CS -- PE10 片选
CONVST -- PE11
BUSY -- PE12
RESET -- PB11
DB0~DB15 -- PD0~PD15
*/
/* Pin define */
#define AD7616_RESET_0 gpio_bit_reset(GPIOB,GPIO_PIN_11)
#define AD7616_RESET_1 gpio_bit_set(GPIOB,GPIO_PIN_11)
#define AD7616_RD_0 gpio_bit_reset(GPIOE,GPIO_PIN_9)
#define AD7616_RD_1 gpio_bit_set(GPIOE,GPIO_PIN_9)
#define AD7616_CS_0 gpio_bit_reset(GPIOE,GPIO_PIN_10)
#define AD7616_CS_1 gpio_bit_set(GPIOE,GPIO_PIN_10)
#define AD7616_CONVST_0 gpio_bit_reset(GPIOE,GPIO_PIN_11)
#define AD7616_CONVST_1 gpio_bit_set(GPIOE,GPIO_PIN_11)
#define AD7616_BUSY_1 gpio_bit_set(GPIOE,GPIO_PIN_12)
#define AD7616_BUSY_LEVEL() gpio_input_bit_get(GPIOE,GPIO_PIN_12)
#define AD7616_INPUT_DB(x) gpio_input_bit_get(GPIOD,BIT(x))//数据引脚0-15
#define AD7616_GET_DATA() gpio_input_port_get(GPIOD) //获取PE端口值
#define AD7616_CHANNEL_VAL 16
#define AD7616_ALL_CH 16
#define AD7616_V0A 0
#define AD7616_V0B 1
#define AD7616_V1A 2
#define AD7616_V1B 3
#define AD7616_V2A 4
#define AD7616_V2B 5
#define AD7616_V3A 6
#define AD7616_V3B 7
#define AD7616_V4A 8
#define AD7616_V4B 9
#define AD7616_V5A 10
#define AD7616_V5B 11
#define AD7616_V6A 12
#define AD7616_V6B 13
#define AD7616_V7A 14
#define AD7616_V7B 15
#define AD7616_ALL_RESER 0 //完全复位
#define AD7616_PART_RESER 1 //部分复位
#define AD7616_AD_GET_COUNT 6//转换次数
#define AD7616_STATE_UNREADABLE 0 //转换开始
#define AD7616_STATE_READABLE 1 //可读
/* parameter */
/*
AD7616结构体参数
temp=A*x*x + B*x + C
*/
typedef struct
{
uint32_t number;//通道编号
float A;
float B;
float C;
}AD7616_parameter;
extern AD7616_parameter AD7616_CH_member[AD7616_CHANNEL_VAL];
extern uint16_t AD7616_Data[AD7616_CHANNEL_VAL];//AD值
extern uint16_t AD7616_AD[AD7616_CHANNEL_VAL];//AD平均值
extern float AD7616_Temp[AD7616_CHANNEL_VAL];//温度值
/* function */
void AD7616_Init(void);
void AD7616_StartConvst(void);//启动转换
ErrStatus AD7616_Convst_Average(const uint32_t count);//启动多次转换并取平均值
#endif
3.5实验结果
未接温度传感器时,所有通道输出最大值0x7FFF(第15位为符号位)。
接入一个热敏电阻时,采集到的AD值便随温度改变,可通过校准,把AD值转换为实际温度。
4.总结
本文为学习AD7616采集数据所做,数据手册很长,并不是单看时序图就能完成驱动编写,但看懂了数据手册之后,就会发现代码挺简单的。需要注意的便是BUSY引脚,它只有输出状态,不要被(手动模式)干扰。在CONVST引脚上升沿时,BUSY便会自动变为高电平,数据转换完成时,BUSY变为低电平。
本文中没有提供主函数代码,需要读取数据时,直接调用
ErrStatus AD7616_Convst_Average(const uint32_t count);启动多次转换函数,然后直接从数组中依次读取数据(AD值或温度值)即可。校准值需要自己实际测量写入。
最后,文章中可能存在着问题,欢迎各位大佬不吝赐教。
2022年11月29日