目录
实验要求:
通过stm32ADC采集50hz交流电,并通过串口显示
实验器材:
芯片为stm32f103RC的野火stm32MINI系列
实验思路:
通过TIM(定时器)触发ADC进行数据采样,将采集到的数据经过DMA转化到指定数组中。在进行一次采样过后,遍历数组经行数据的读取,并确定最大值与最小值,通过最大值与最小的差求得峰峰值。通过数学运算进而得到有效值与峰值。
注意:发生器显示的电压为峰峰值,在没有连接外部电路时,单片机只能采集到0~3.3V,因此单片得到的数据电压乘二即为发生器显示的电压。
实验步骤:
1。编写ADC配置:
static void ADCx_Mode_Config(void)
{
ADC_InitTypeDef ADC_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE); //使能ADC1通道时钟
//ADC1初始化
ADC_InitStructure.ADC_Mode = ADC_Mode_Independent; //独立ADC模式
ADC_InitStructure.ADC_ScanConvMode = DISABLE; //关闭扫描方式
ADC_InitStructure.ADC_ContinuousConvMode = DISABLE; //关闭连续转换模式
ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_T2_CC2; //使用外部触发模式
ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right; //采集数据右对齐
ADC_InitStructure.ADC_NbrOfChannel = 1; //要转换的通道数目
ADC_Init(ADCx, &ADC_InitStructure);
RCC_ADCCLKConfig(RCC_PCLK2_Div6); //配置ADC时钟,为PCLK2的6分频,即12Hz
ADC_RegularChannelConfig(ADCx, ADC_Channel_6, 1, ADC_SampleTime_239Cycles5); //配置ADC1通道6为239.5个采样周期
//使能ADC、DMA
ADC_DMACmd(ADCx,ENABLE);
ADC_Cmd(ADCx,ENABLE);
ADC_ResetCalibration(ADCx); //复位校准寄存器
while(ADC_GetResetCalibrationStatus(ADCx)); //等待校准寄存器复位完成
ADC_StartCalibration(ADCx); //ADC校准
while(ADC_GetCalibrationStatus(ADCx)); //等待校准完成
ADC_ExternalTrigConvCmd(ADCx, ENABLE); //设置外部触发模式使能
}
2.DMA配置:
/* DMA1配置 */
void DMAx_Mode_Config()
{
DMA_InitTypeDef DMA_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
RCC_AHBPeriphClockCmd(DMA_CLK,ENABLE); //使能ADC1通道时钟
//DMA1初始化
DMA_DeInit(DMA_CHANNE);
DMA_InitStructure.DMA_PeripheralBaseAddr = ( u32 ) ( & ( ADCx->DR ) ); //ADC1地址
DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)&ADC_ConvertedValue; //内存地址
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC; //方向(从外设到内存)
DMA_InitStructure.DMA_BufferSize = (uint32_t)DMA_LEN; //传输内容的大小
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; //外设地址固定
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable; //内存地址递增
DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord ; //外设数据单位
DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord ; //内存数据单位
DMA_InitStructure.DMA_Mode = DMA_Mode_Circular ; //DMA模式:循环传输
DMA_InitStructure.DMA_Priority = DMA_Priority_High ; //优先级:高
DMA_InitStructure.DMA_M2M = DMA_M2M_Disable; //禁止内存到内存的传输
DMA_Init(DMA_CHANNE, &DMA_InitStructure); //配置DMA1
DMA_ITConfig(DMA_CHANNE,DMA_IT_TC, ENABLE); //使能传输完成中断
NVIC_InitStructure.NVIC_IRQChannel = DMA_IRQN; //中断源设置
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0; //优先级抢占
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; //子优先级
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //使能中断
NVIC_Init(&NVIC_InitStructure);
DMA_Cmd(DMA_CHANNE,ENABLE);
}
3.ADC_GPIO设置
//GPIO配置,PA6
void GPIO_Mode_Config()
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(GPIO_CLK, ENABLE); //使能GPIOA时钟
//PA6 作为模拟通道输入引脚
GPIO_InitStructure.GPIO_Pin = GPIO_PIN;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;
GPIO_Init(GPIO_PORT, &GPIO_InitStructure);
}
4.定时器配置
/* TIM2配置 */
void TIMx_Mode_Config(u16 arr,u16 psc) //arr为重加载值,psc为预分频系数
{
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
TIM_OCInitTypeDef TIM_OCInitStructure;
RCC_APB1PeriphClockCmd(TIM_CLK, ENABLE); //时钟使能
//定时器TIM2初始化
TIM_TimeBaseStructure.TIM_Period = arr; //设置在下一个更新事件装入活动的自动重装载寄存器周期的值
TIM_TimeBaseStructure.TIM_Prescaler =psc; //设置用来作为TIMx时钟频率除数的预分频值
TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1; //设置时钟分割:TDTS = Tck_tim
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; //TIM向上计数模式
TIM_TimeBaseInit(TIMx, &TIM_TimeBaseStructure); //根据指定的参数初始化TIMx的时间基数单位
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1; //选择定时器模式:TIM脉冲宽度调制模式1
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; //比较输出使能
TIM_OCInitStructure.TIM_Pulse = TIM_PULSE; //脉搏宽度
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_Low; //输出极性:TIM输出比较极性低
TIM_OC2Init(TIMx, & TIM_OCInitStructure); //初始化外设TIM2_CH2
TIM_Cmd(TIMx, ENABLE); //使能TIMx
TIM_CtrlPWMOutputs(TIMx, ENABLE);
}
5.DAM终端配置
void DMA1_Channel1_IRQHandler(void)
{
uint8_t i;
uint16_t max=0;
uint16_t min=4096;
if(DMA_GetITStatus(DMA1_IT_TC1)!=RESET)
{
ADC_Cmd(ADC1, DISABLE); //不使能ADC
for(i=0;i<FFT_LENGTH;i++)
{
if(max<ADC_ConvertedValue[i])
{
max=ADC_ConvertedValue[i];
}
if(min>ADC_ConvertedValue[i])
{
min=ADC_ConvertedValue[i];
}
}
fengzhi=(float)(max-min)*(3.3/4096);
printf("峰值为:%.2f\r\n",fengzhi);
printf("有效值为:%.2f\r\n",(fengzhi/sqrt(2.0)));
ADC_Cmd(ADC1, ENABLE);//使能ADC
}
DMA_ClearITPendingBit(DMA1_IT_TC1);
}
6.其他相关函数的配置
/* 数据定义 */
__IO uint16_t ADC_ConvertedValue[ADC_LEN] = {0}; // 采集数据存放的数据
float fengzhi = 0; //峰值的定义
//float youxiao = 0; //有效值通过峰值除以sqr(2)得到,暂未使用变量
//float Rms = 0;
7。初始化
/* 初始化 */
void Adc_Init()
{
TIMx_Mode_Config(100,71); //(72000000/71+1)/(100+1)=10000HZ;
ADCx_Mode_Config();
DMAx_Mode_Config();
GPIO_Mode_Config();
}
以上均写在ADC.c文件中
ADC.h 文件的编写
#ifndef __BSP_ADC_H
#define __BSP_ADC_H
#include "stm32f10x.h"
#include "stdio.h"
#include "math.h"
/* 通用定义 */
#define FFT_LENGTH 200
/* ADC相关引脚定义 */
#define ADCx ADC1
#define ADC_LEN 200
#define ADC_CLK RCC_APB2Periph_ADC1
/* ADC输入引脚定义 */
#define GPIO_PORT GPIOC
#define GPIO_PIN GPIO_Pin_1
#define GPIO_CLK RCC_APB2Periph_GPIOC
/* DAM相关配置 */
#define DMA_CLK RCC_AHBPeriph_DMA1
#define DMA_CHANNE DMA1_Channel1
#define DMA_IRQN DMA1_Channel1_IRQn;
#define DMA_LEN 200
/* TIM相关配置 */
#define TIMx TIM2
#define TIM_PULSE 50
#define TIM_CLK RCC_APB1Periph_TIM2
/* 函数定义 */
void Adc_Init(void);
#endif /*__BSP__ADC_H*/
主函数只需在main函数中加入 Adc.Init() 即可
总结:
本次实验囊括内容较多,通过本次实验加强了对ADC、DMA、TIM函数的认识,学习的内容也进一步扩大。注重章节的点点滴滴,脚踏实地的出发才能稳扎知识。
程序源码:
输入引脚要看代码,目前设置为PC1,即将发生器函数的红线接入PA6
文件集合访问 密码:74qn
注:本文代码借鉴于网络,本人只做CTRl+C与CTRl+V并对代码进行需求修改,侵删!
版权声明:本文为CSDN博主「没有星的恒星」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/qq_47070811/article/details/121371473
暂无评论