📄 dso.c
字号:
/****************************************************************************
* Copyright (C), 2010, www.armfly.com
*
* 【本例程在安富莱STM32F103ZE-EK开发板上调试通过 】
* 【QQ: 1295744630, 旺旺:armfly, Email: armfly@qq.com 】
*
* 文件名: dso.c
* 内容简述: 本模块完成示波器adc采样方面的工作
*
* 文件历史:
* 版本号 日期 作者 说明
* v0.1 2010-03-18 armfly 创建该文件
*
*/
/* Includes ------------------------------------------------------------------*/
#include "stm32f10x.h"
#include <stdio.h>
#include <string.h>
#include "tft_lcd.h"
#include "systick.h"
#include "dso.h"
#include "button.h"
#include "BackLight.h"
#include "WaveMaker.h"
#define DSO_VER "V0.6" /* 当前版本 */
/* 主程序状态字 */
enum
{
S_HELP = 0, /* 帮助页面 */
S_RUN = 1, /* 采集状态 */
S_HOLD = 2 /* 暂停状态 */
};
/* 时间分度表, g_DSO.TimeBaseId作为数组索引 */
const uint32_t g_TimeTable[] =
{
20, 50, 100,
200, 500, 1000,
2000, 5000, 10000,
20000, 50000, 100000,
200000, 500000, 1000000
};
/* 衰减倍数表
g_DSO.Ch1Attenuation = 23
幅度衰减因子:
(1) 28 === 1V (1:1)
*/
#define ATT_COUNT 6
const uint32_t g_AttTable[ATT_COUNT][2] =
{
/* {除数,每大格电压} 1:1 */
{1400, 5000},
{560, 2000},
{280, 1000},
{140, 500},
{56, 200},
{28, 100},
};
static void DsoHelp(uint8_t *pMainStatus);
static void DsoRun(uint8_t *pMainStatus);
/*
安富莱STM32F103ZE-EK 口线分配:
示波器通道1同时连接到CPU的PC0、PC2、PC3,这3个口线作为ADC输入使用
PC0 = ADC1_IN10
PC2 = ADC2_IN12
PC3 = ADC3_IN13
示波器通道2只连接到CPU的PC1,这个口线作为ADC输入使用
PC1 = ADC2_IN12
对于通道1的模拟信号,可以同时启动3个ADC进行交错采样,理论上可以实现3Mbps采样速率
(后来发现ADC2不能DMA操作,因此这句话还有待考究)
通道2只能启动1个ADC进行采样。
支持通道1和通道2进行同步采样。
*/
DSO_T g_DSO; /* 全局变量,是一个结构体 */
/*******************************************************************************
* 函数名: DsoMain
* 输 入: 无
* 输 出: 无
* 功能说明:DSO主程序,是一个循环体
*/
void DsoMain(void)
{
uint8_t MainStatus = S_RUN; /* 程序执行状态 */
InitWaveMaker();
// SetupWaveType(WT_SIN); /* 设置正弦波 */
LCD_Init(); /* 显示器初始化 */
SetBackLight(BRIGHT_DEFAULT); /* 设置背光亮度 */
/* 进入主程序循环体 */
while (1)
{
switch (MainStatus)
{
case S_HELP:
DsoHelp(&MainStatus); /* 显示帮助 */
break;
case S_RUN:
DsoRun(&MainStatus); /* 全速采集,实时显示 */
break;
default:
break;
}
}
}
/*******************************************************************************
* 函数名: InitDSO
* 输 入: 无
* 输 出: 无
* 功能说明:对示波器通道1进行初始化配置。主要完成GPIO的配置、ADC的配置、
* 定时器的配置以及DMA的配置。
*/
static void InitDSO(void)
{
/* 配置GPIO. */
{
GPIO_InitTypeDef GPIO_InitStructure;
/* 打开GPIO_C 和 AFIO 的时钟 */
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC | RCC_APB2Periph_AFIO, ENABLE);
/* 配置PC0为模拟输入模式 */
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOC, &GPIO_InitStructure);
}
/* 配置DMA1_1 用于CH1 */
#if 1
{
DMA_InitTypeDef DMA_InitStructure;
/* Enable DMA1 clock */
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);
DMA_DeInit(DMA1_Channel1); /* 复位DMA1寄存器到缺省状态 */
DMA_InitStructure.DMA_PeripheralBaseAddr = ADC1_DR_Address; /* 选择ADC1的数据寄存器作为源 */
DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)&g_DSO.Ch1Buf; /* 目标地址 */
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC; /* 设置DMA传输方向,外设(ADC)作为源 */
DMA_InitStructure.DMA_BufferSize = SAMPLE_COUNT; /* 设置缓冲区大小 */
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; /* 外设地址不自增 */
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable; /* 存储器地址需要自增 */
DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord; /* 选择外设传输单位:16bit */
DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord; /* 选择内存传输单位:16bit */
DMA_InitStructure.DMA_Mode = DMA_Mode_Normal; /* 无需循环模式 */
DMA_InitStructure.DMA_Priority = DMA_Priority_High; /* 选择DMA优先级 */
DMA_InitStructure.DMA_M2M = DMA_M2M_Disable; /* DMA传输类型,不是内存到内存 */
DMA_Init(DMA1_Channel1, &DMA_InitStructure);
/* Enable DMA1 channel1 */
DMA_Cmd(DMA1_Channel1, ENABLE);
}
#endif
/* 配置DMA2_5 用于CH2 */
#if 1
{
DMA_InitTypeDef DMA_InitStructure;
/* Enable DMA1 clock */
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA2, ENABLE);
DMA_DeInit(DMA2_Channel5); /* 复位DMA2寄存器到缺省状态 */
DMA_InitStructure.DMA_PeripheralBaseAddr = ADC3_DR_Address; /* 选择ADC3的数据寄存器作为源 */
DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)&g_DSO.Ch2Buf; /* 目标地址 */
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC; /* 设置DMA传输方向,外设(ADC)作为源 */
DMA_InitStructure.DMA_BufferSize = SAMPLE_COUNT; /* 设置缓冲区大小 */
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; /* 外设地址不自增 */
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable; /* 存储器地址需要自增 */
DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord; /* 选择外设传输单位:16bit */
DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord; /* 选择内存传输单位:16bit */
DMA_InitStructure.DMA_Mode = DMA_Mode_Normal; /* 无需循环模式 */
DMA_InitStructure.DMA_Priority = DMA_Priority_Medium; /* 选择DMA优先级 */
DMA_InitStructure.DMA_M2M = DMA_M2M_Disable; /* DMA传输类型,不是内存到内存 */
DMA_Init(DMA2_Channel5, &DMA_InitStructure);
/* Enable DMA2 channel5 */
DMA_Cmd(DMA2_Channel5, ENABLE);
}
#endif
/* 配置ADC1 */
/* ADCCLK = PCLK2/2 */
RCC_ADCCLKConfig(RCC_PCLK2_Div2); //ADCCLK = PCLK2/4 = 18MHz
#if 1
{
ADC_InitTypeDef ADC_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE);
ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;
ADC_InitStructure.ADC_ScanConvMode = DISABLE;
ADC_InitStructure.ADC_ContinuousConvMode = DISABLE; /* 连续转换静止 */
ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_T1_CC3; /* 选择TIM1的CC3做触发 */
//ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_T2_CC2; /* 选择TIM2的CC2做触发 */
ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right; /* 数据右对齐,高位为0 */
ADC_InitStructure.ADC_NbrOfChannel = 1; /* 1个通道 */
ADC_Init(ADC1, &ADC_InitStructure);
/* ADC1 regular channels configuration */
ADC_RegularChannelConfig(ADC1, ADC_Channel_10, 1, ADC_SampleTime_1Cycles5);
/* Enable ADC1 external trigger conversion */
ADC_ExternalTrigConvCmd(ADC1, ENABLE);
/* 使能 ADC1 */
ADC_Cmd(ADC1, ENABLE);
/* Enable ADC1 DMA */
ADC_DMACmd(ADC1, ENABLE);
/* 使能 ADC1 复位校准寄存器 */
ADC_ResetCalibration(ADC1);
/* 检测复位校准寄存器 */
while(ADC_GetResetCalibrationStatus(ADC1));
/* 开始 ADC1 校准 */
ADC_StartCalibration(ADC1);
/* 等待校准结束 */
while(ADC_GetCalibrationStatus(ADC1));
}
#endif
/* 配置ADC3 */
#if 1
{
ADC_InitTypeDef ADC_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC3, ENABLE);
ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;
ADC_InitStructure.ADC_ScanConvMode = DISABLE;
ADC_InitStructure.ADC_ContinuousConvMode = DISABLE; /* 连续转换静止 */
//ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_T1_CC1; /* 选择TIM1的CC1做触发 */
ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_T1_CC3; /* 选择TIM2的CC2做触发 */
ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right; /* 数据右对齐,高位为0 */
ADC_InitStructure.ADC_NbrOfChannel = 1; /* 1个通道 */
ADC_Init(ADC3, &ADC_InitStructure);
/* ADC1 regular channels configuration */
ADC_RegularChannelConfig(ADC3, ADC_Channel_11, 1, ADC_SampleTime_1Cycles5);
/* Enable ADC3 external trigger conversion */
ADC_ExternalTrigConvCmd(ADC3, ENABLE);
/* 使能 ADC3 */
ADC_Cmd(ADC3, ENABLE);
/* Enable ADC3 DMA */
ADC_DMACmd(ADC3, ENABLE);
/* 使能 ADC3 复位校准寄存器 */
ADC_ResetCalibration(ADC3);
/* 检测复位校准寄存器 */
while(ADC_GetResetCalibrationStatus(ADC3));
/* 开始 ADC3 校准 */
ADC_StartCalibration(ADC3);
/* 等待校准结束 */
while(ADC_GetCalibrationStatus(ADC3));
}
#endif
/* 配置采样触发定时器,使用TIM1 CC1 */
SetSampRate(g_DSO.SampleFreq); /* 修改采样频率(启动时100K) */
}
/*******************************************************************************
* 函数名: SetSampRate
* 输 入: freq : 采样频率 单位Hz
* 输 出:
* 功 能:修改采样频率
*/
static void SetSampRate(uint32_t freq)
{
/* 第4步: 配置定时器2第2通道CC2, CC2作为ADC1的触发源 */
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
TIM_OCInitTypeDef TIM_OCInitStructure;
uint16_t Period;
TIM_Cmd(TIM1, DISABLE);
// g_DSO.SampleRate = freq; /* 将采样频率保存到全部变量中 */
/*
采样频率计算公式 :
period = 72000000 / freq ;
1200 == 60KHz 采样频率
*/
Period = 72000000 / freq;
/* Enable TIM1 clock */
RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM1, ENABLE);
TIM_TimeBaseStructInit(&TIM_TimeBaseStructure);
TIM_TimeBaseStructure.TIM_Period = Period;
TIM_TimeBaseStructure.TIM_Prescaler = 0; /* 计数频率 = 72000 000 / 18 = 4000 000 */
TIM_TimeBaseStructure.TIM_ClockDivision = 0x0;
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInit(TIM1, &TIM_TimeBaseStructure);
/* TIM1 channel1 configuration in PWM mode */
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
TIM_OCInitStructure.TIM_Pulse = TIM_TimeBaseStructure.TIM_Period / 2;
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_Low;
TIM_OC3Init(TIM1, &TIM_OCInitStructure);
/* TIM1 counter enable */
TIM_Cmd(TIM1, ENABLE);
/* TIM1 main Output Enable */
TIM_CtrlPWMOutputs(TIM1, ENABLE);
}
static void DispHelp1(void)
{
uint16_t y;
uint16_t LineCap;
LCD_Clear(Blue); /* 清屏,背景蓝色 */
LCD_SetBackColor(Blue); /* 设置文字背景颜色 */
LCD_SetTextColor(White); /* 设置文字颜色 */
y = 0;
LineCap = 18; /* 行间距 */
LCD_DisplayString(20, y, "安富莱STM32F103ZE-EK开发板 www.armfly.com");
y += LineCap;
LCD_SetTextColor(Yellow);
LCD_DisplayString(30, y, "QQ:1295744630 Email:armfly@qq.com");
y += LineCap;
y += LineCap;
LCD_DisplayString(30, y, "操作提示:");
y += LineCap;
LCD_DisplayString(50, y, "TAMPER键 = 切换通道焦点。CH1或CH2");
y += LineCap;
LCD_DisplayString(50, y, "WAKEUP键 = 显示帮助或退出帮助");
y += LineCap;
LCD_DisplayString(50, y, "USER 键 = 暂停或实时运行");
y += LineCap;
LCD_DisplayString(50, y, "摇杆上键 = 放大波形垂直幅度或向上移动");
y += LineCap;
LCD_DisplayString(50, y, "摇杆下键 = 缩小波形垂直幅度或向下移动");
y += LineCap;
LCD_DisplayString(50, y, "摇杆左键 = 水平展宽波形");
y += LineCap;
LCD_DisplayString(50, y, "摇杆右键 = 水平缩小波形");
y += LineCap;
LCD_DisplayString(50, y, "摇杆OK键 = 切换摇杆调节模式。幅度或位置");
y += LineCap;
}
/*******************************************************************************
* 函数名: DsoHelp
* 输 入: 无
* 输 出: 无
* 功 能:显示帮助
*/
static void DsoHelp(uint8_t *pMainStatus)
{
uint8_t KeyCode;
uint16_t bright;
uint8_t fRefresh = 1; /* LCD刷新标志 */
uint8_t SubStatus = 0;
bright = GetBackLight(); /* 读取背光值 */
while (*pMainStatus == S_HELP)
{
CPU_IDLE();
if (fRefresh)
{
fRefresh = 0;
if (SubStatus == 0)
{
DispHelp1();
}
}
/* 读取按键,大于0表示有键按下 */
KeyCode = GetKey();
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -