09 ADC 电压采集实验

开发板官网(https://www.edevkit.com/)
本系列教程目录:
- 瑞萨单片机开发教程[一] e2-studio + DAP LINK开发环境搭建
- 瑞萨单片机开发教程[二] Keil/RASC开发环境搭建
- 瑞萨单片机开发教程[三] 新建RASC工程
- 瑞萨单片机开发教程[四] GPIO输出——led实验
- 瑞萨单片机开发教程[五] GPIO输入——按键key实验
- 瑞萨单片机开发教程[六] ICU外部中断实验
- 瑞萨单片机开发教程[七] SCI UART串口通信实验
- 瑞萨单片机开发教程[八] DMAC 串口数据传输实验
- 瑞萨单片机开发教程[九] ADC 电压采集实验
ADC 电压采集实验
本章目标:
- 了解 ADC 基本概念和 RA6M5 处理器的 ADC 模块;
- 学会使用 RASC 配置 ADC 采样引脚的模拟信号;
硬件设计
本实验使用开发板的 ADC 模块,通过 ADC 通道采样外部电压信号,并由串口输出采集数据。
软件设计
新建工程
复制一份“07 SCI-UART_Recv_send”修改文件夹名“09 ADC”,双击打开 EBF_RA6M5.uvprojx 工程,通过 Keil 的新建文件新建 adc.c 和 adc.h,保存到目录下的 \src 文件夹下。
FSP 配置
Keil 界面选择“Tools”→“RA Smart Configurator”打开 RASC 界面。
首先配置 ADC 通道,依次点击“Stacks”→“Pins”→“Peripherals”→“ADC0”来配置通道 AN000 对应的引脚为 P000。

下一步点击“Stacks”→“New Stack”→“Analog”→“ADC (r_adc)”来添加 ADC 模块。

下一步配置 ADC 通道 0 的属性。

在“Input”→“Channel Scan Mask”选择通道 0。
| ADC属性 | 描述 |
|---|---|
| General > Name | 模块实例名 |
| General > Unit | 指定要使用的 ADC 单元 |
| General > Resolution | 指定转换分辨率 |
| General > Alignment | 指定转换结果对齐方式 |
| General > Clear after read | 读取转换结果后自动清除 |
| General > Mode | 模式 Single Scan:单次扫描 Continuous Scan:连续扫描 Group Scan:组扫描 |
| General > Double-trigger | 双触发 |
| Input > Channel Scan Mask (channel availability varies by MCU) | 该通道位掩码用于使能 ADC 通道; 若是在组模式下,则是用于指定哪些通道归属于扫描组 A。 这里要勾选 “Channel 0”,图上由于篇幅关系没有标出来 |
| Interrupts > Normal/Group A Trigger | 正常模式下的或组模式下组 A 的触发类型。 这里按照默认,选择软件触发 |
| Interrupts > Callback | 中断回调函数。设置为 adc_callback |
| Interrupts > Scan End Interrupt Priority | 扫描完成中断优先级 |
| Extra > ADC Ring Buffer | ADC 环形缓冲区 |
配置完成之后可以按下快捷键“Ctrl + S”保存,最后点击 “Generate Project Content” 按钮,让软件自动生成配置代码即可。
- 引脚配置信息
引脚配置还是在 pin_data.c 文件里生成。
const ioport_pin_cfg_t g_bsp_pin_cfg_data[] = {
{
.pin = BSP_IO_PORT_00_PIN_00,
.pin_cfg = ((uint32_t) IOPORT_CFG_ANALOG_ENABLE)
},
}
将 P000 复用为 IOPORT_CFG_ANALOG_ENABLE,也就是使能了 P000 的模拟量输入功能。
- ADC配置信息
ADC 设备对象会在 hal_data.c 中生成,名为 g_adc,
const adc_instance_t g_adc0 =
{
.p_ctrl = &g_adc0_ctrl,
.p_cfg = &g_adc0_cfg,
.p_channel_cfg = &g_adc0_channel_cfg,
.p_api = &g_adc_on_adc
};
p_ctrl:它被用来保存 ADC 的信息,比如状态、寄存器、回调函数等。
p_cfg:指向 ADC 模块的配置信息结构体 g_adc0_cfg。
const adc_cfg_t g_adc0_cfg =
{
.unit = 0,
.mode = ADC_MODE_SINGLE_SCAN,
.resolution = ADC_RESOLUTION_12_BIT,
.alignment = (adc_alignment_t) ADC_ALIGNMENT_RIGHT,
.trigger = (adc_trigger_t)0xF, // Not used
.p_callback = adc_callback,
}
p_channel_cfg:指向某个通道的配置值,也是来自 RASC 的配置。
const adc_channel_cfg_t g_adc0_channel_cfg =
{
.scan_mask = ADC_MASK_CHANNEL_0 | 0,
.scan_mask_group_b = 0,
.priority_group_a = ADC_GROUP_A_PRIORITY_OFF,
.add_mask = 0,
.sample_hold_mask = 0,
.sample_hold_states = 24,
#if ((0) | (0))
.p_window_cfg = (adc_window_cfg_t *) &g_adc0_window_cfg,
#else
.p_window_cfg = NULL,
#endif
};
p_api:ADC 模块的接口,指向在 r_adc.c 中实现的 g_adc_on_adc 结构体。用户可以使用 g_adc.p_api 中的函数指针来操作 ADC。
程序设计
实现 ADC 读取通道的电压值,ADC 通道 0 连接 P000 引脚,在 P000 用杜邦线接 3.3V,开启转换,读取 ADC 值,换算成电压值,通过串口 printf 发送格式化数据字符串,在串口助手观察数据。
- 打开 ADC 设备
R_ADC_Open(adc_ctrl_t * const p_ctrl, adc_cfg_t const * const p_cfg);
open 函数初始化 ADC,比如根据 p_cfg 来配置 ADC 的寄存器,设置中断回调函数等。但是这个函数没有完成对采样通道的初始化,采样通道的初始化需要调用 scanCfg 函数。
- 配置 ADC 扫描参数
R_ADC_ScanCfg(adc_ctrl_t * const p_ctrl, void const * const p_extend);
在调用 open 函数初始化了 ADC 后,还需要调用 scanCfg 配置指定的采样通道。第二个参数传入 g_adc 的 p_channel_cfg 成员。
- 开启扫描
R_ADC_ScanStart(adc_ctrl_t * const p_ctrl);
- 读取 ADC 数据
R_ADC_Read(adc_ctrl_t * const p_ctrl, adc_channel_t const reg_id, uint16_t * const p_data);
用户通过 read 函数直接读取到 ADC 采样到的 12bit 的有效值。也可以调用 read32 函数读出采样值并转化为 32bit 的数据。
- 读取 ADC 转换状态
R_ADC_StatusGet(adc_ctrl_t * const p_ctrl, adc_status_t * p_status);
如果未启用中断,则可使用 R_ADC_StatusGet() 用于轮询 ADC 以确定扫描何时完成。读取 API 函数用于访问转换后的 ADC 结果。这适用于支持校准的 MCU 的普通扫描和校准扫描。
- ADC 转换完成中断回调
void adc_callback(adc_callback_args_t * p_args)
通过 ADC 的中断回调函数来判断 ADC 是否转换完成。我们需要定义一个布尔类型的数据 scan_complete_flag 来当做 ADC 读取完成的标志位。当没有转换完成的时候 scan_complete_flag 的值一直为 false,但 ADC 触发中断的时候将 scan_complete_flag 的值变为 true。
- ADC 值转换电压
v = (double)(adc_data*3.3/4095);
ADC 原始数据转换为电压值(ADC 参考电压为 3.3V),ADC 满量程值是 4095,对应 3.3V 电压。
程序编写
在 adc.h 头文件里定义中断初始化函数,数据读取函数。
#ifndef __BSP_ADC_H
#define __BSP_ADC_H
#include "hal_data.h"
void adc_Init(void);
double read_ADC_Value(void);
#endif
在 adc.c 里实现初始化函数、中断回调函数,数据读取函数。
#include "adc.h"
//ADC转换完成标志位
volatile bool scan_complete_flag = false;
//ADC转换完成中断
void adc_callback(adc_callback_args_t * p_args)
{
FSP_PARAMETER_NOT_USED(p_args);
scan_complete_flag = true;
}
//ADC初始化
void adc_Init(void)
{
fsp_err_t err;
err = R_ADC_Open(&g_adc0_ctrl, &g_adc0_cfg);
err = R_ADC_ScanCfg(&g_adc0_ctrl, &g_adc0_channel_cfg);
assert(FSP_SUCCESS == err);
}
/* 进行ADC采集,读取ADC数据并转换结果 */
double read_ADC_Value(void)
{
uint16_t adc_data;
double v;
(void)R_ADC_ScanStart(&g_adc0_ctrl);
while (!scan_complete_flag) //等待转换完成标志
{
;
}
scan_complete_flag = false; //重新清除标志位
/* 读取通道0数据 */
R_ADC_Read(&g_adc0_ctrl, ADC_CHANNEL_0, &adc_data);
/* ADC原始数据转换为电压值(ADC参考电压为3.3V) */
v = (double)(adc_data*3.3/4095);
return v;
}
在 hal_entry.c 文件中添加头文件 adc.h,在 hal_entry 入口函数初始化 adc,并添加调试语句,循环中 1s 执行一次读数据操作,并打印数据到串口。
void hal_entry(void)
{
/* TODO: add your own code here */
led_Init(); // LED 初始化
uart0_Init();
adc_Init();
printf("start ADC scan\r\n");
while(1)
{
LED1_TOGGLE();
R_BSP_SoftwareDelay(1, BSP_DELAY_UNITS_SECONDS); //延时1秒
float adcV = (float)read_ADC_Value();
printf("ADC to v: %.2f\r\n", adcV);
}
#if BSP_TZ_SECURE_BUILD
/* Enter non-secure code */
R_BSP_NonSecureEnter();
#endif
}
下载验证
首先通过 USB 串口连接电脑和开发板,串口的 RX/TX 分别连接开发板的 TX/RX,交叉连接,电脑端打开串口调试助手,波特率设置 115200。
连接下载器,将程序编译并下载到开发板之后,按下复位按键来复位开发板,程序启动初始化完会打印数据。
把 P000 引脚用杜邦线连接开发板的 3.3V 引脚,数据打印显示是 3.3V。

把 P000 引脚用杜邦线连接开发板的 GND 引脚,数据打印显示是 0V。
