# 安信可PB-02模组评测(4)——PHY62XX ADC使用指南
## 本篇文章于EEWORLD首发,详情移步:[EEWORLD评测](https://bbs.eeworld.com.cn/elecplay/content/189#F5)
- 软件环境:SDK
- 硬件:安信可PB-02kit
## PHY6212 ADC介绍
PHY62XX ADC 一共有 9 个 ADC 通道:1 个 PGA,1 个 Temp Sensor,6 个 Normal ADC,1 个 Voice。
有意思的是**ADC_CH0** 是**PGA**,PGA 是何许东西??让我介绍一下:
### PGA:
**可编程增益放大器**(PGA),可编程增益放大器含全平衡差动放大器模块、译码器模块和电阻开关阵列模块,全平衡差动放大器模块中的负反馈电阻分压器的电阻比确定该放大器的最大增益,通过译码器模块的译码结果控制电阻开关阵列模块衰减输入信号的衰减量,最终实现该放大器的增益的可编程。
好吧上边这一段其实我也看不太懂,我给大家简答说两句,提取一下关键信息,一般多通道数据采集系统用很多不同类型的传感器,这包括热电偶、电桥,热敏电阻、应变计和超声系统。虽然,传感器是基于不同的物理原理,但大多数产品是以电压做为输出。在工业过程控制系统中低频信号可以到几毫伏到几伏变化,这时候就应用到了PGA来将其匹配到特定的ADC输入范围,12位ADC接收小于ADC满标输入十分之一的信号仅可提供8位分辨率,除非在信号到达ADC之前用PGA放大。但是问题也来了手册这里介绍的并不充分,也没介绍怎么用,大家了解一下就好啦。
### 工作模式:
在 6 个 Normal ADC 通道中,每一个ADC都有两种工作模式分别是
- 单端
- 差分
在单端模式下每个采集的就是其引脚上的单端电压,如果要采用差分模式,:**ADC_CH2~ADC_CH3**、**ADC_CH4~ADC_CH5**、**ADC_CH6~ADC_CH7** 要成对出 现,采集的电压是 **ADC_CH2**、**ADC_CH4**、**ADC_CH6** 上相对于 **ADC_CH3**、**ADC_CH5**、 **ADC_CH7** 上的差分电压。这样抗噪声性能更好更强。(值得注意的是,工作时候只能配置一对为差分模式)。
### 输入范围:
- bypass mode:【0V,1V】;
- attenuation mode:【0V,4V】;实际显示电压不超过芯片 **AVDD33**引脚电压。
### 采集精度:
在不同范围、模式下ADC采集的精度也大有不同具体情况如下表:
### 采集速率:
Normal ADC 支持采样速率有:80K,160K,320K,默认速率为 320K。ADC的速率并不是很快这可能也和内部机制有关系。
## PHY6212 ADC实战分析:
工作方式有两种
- 中断
- 轮询
在大多数情况下都是使用中断来触发采集信号,这里主要介绍中断的方式。
### 中断方式:
9 个 ADC 通道共用一个中断入口,中断号为:**CM0(29)**。 每个 Normal ADC 通道的中断都能单独 mask 和 clear。 中断触发条件为 buffer 满,即数据填满整块 memory 会触发中断。 中断方式软件处理流程如下,很多流程已经被封装到了 API 中,直接调用使用即可。
system initial
2. ADC initial
3. ADC enable
4.irq enable
5. enable ADC interrupt
6. wait interrupt
7. Collect Data
8. calculate ADC value
9.mask interrupt
10. clear interrupt
代码分析:
```c
uint16 adc_ProcessEvent( uint8 task_id, uint16 events )//系统ADC处理函数
{
VOID task_id;
// OSAL 注册时需要的参数,本函数并没有使用
//LOG("adc_ProcessEvent: 0x%x\n",events);
if ( events & SYS_EVENT_MSG )//确认事件发生
{
uint8 *pMsg;
if ( (pMsg = osal_msg_receive( adcDemo_TaskID )) != NULL )
{
adc_ProcessOSALMsg( (osal_event_hdr_t *)pMsg );//这个函数实际是空的
// 事放信号量
VOID osal_msg_deallocate( pMsg );
}
// return unprocessed events
return (events ^ SYS_EVENT_MSG);
}
if ( events & 0x20 )
{
// 配置事件计时器发生
LOG("20\n");
osal_start_timerEx( adcDemo_TaskID, 0x20, 2000);
return (events ^ 0x20);
}
if ( events & adcMeasureTask_EVT )
{
// Perform periodic heart rate task
//LOG("adcMeasureTask_EVT\n");
adcMeasureTask();
return (events ^ adcMeasureTask_EVT);
}
// Discard unknown events
return 0;
}
adc_Cfg_t adc_cfg = {
.channel = ADC_BIT(ADC_CH3P_P20)|ADC_BIT(ADC_CH2P_P14)|ADC_BIT(ADC_CH3N_P15),//设置开启引脚
.is_continue_mode = FALSE,//非连续转换模式
.is_differential_mode = 0x00,//模式0
.is_high_resolution = 0x7F,//精度设置
};
static void adcMeasureTask( void )//实际测量任务
{
int ret;
bool batt_mode = FALSE;
uint8_t batt_ch = ADC_CH3P_P20;
GPIO_Pin_e pin;
if(FALSE == batt_mode) //根据模式进行变换
{
ret = hal_adc_config_channel(adc_cfg, adc_evt);
}
else
{//此处并未开启
if((((1 << batt_ch) & adc_cfg.channel) == 0) || (adc_cfg.is_differential_mode != 0x00))
return;
pin = s_pinmap[batt_ch];
hal_gpio_cfg_analog_io(pin,Bit_DISABLE);//
hal_gpio_write(pin, 1);
ret = hal_adc_config_channel(adc_cfg, adc_evt);
hal_gpio_cfg_analog_io(pin,Bit_DISABLE);
}
if(ret)
{
LOG("ret = %d\n",ret);
return;
}
hal_adc_start();//开启ADC
}
```
开启后烧录代码结果如图: