本帖最后由 丰玉霖 于 2017-5-26 14:26 编辑
这两天老师要求做电赛写单片机的boys开始写一个建议示波器,思路是学校代代祖传的,不过他们都用msp430系列写的,就我用stm32独立完成。写了两天,有一些疑惑,参考了论坛里许多代码,先实现了一个很基础很垃圾的应付检查,写在这里是为了整理自己的凌乱思路,不是什么特别friendly的分享帖,然后有一些问题想问,两个低端的,一个有点难的。
热烈欢迎原子哥以及其他大佬莅临指导!!!@正点原子
代码思路: 通过单通道单次转换得到500个连续的ADC值,通过数据处理,(即与上次显示的值作比对,找到从哪里开始显示波形防止图片不稳)选出200个连续值打在屏幕上连线形成波形。图形的高度可调,采样率可以通过按键调整,对于较高的频率,可以调整往屏幕上打印的规则,即用将屏幕横向拉长,最高能测到20KHz。屏幕下方显示横格对应的时间,纵格对应电压,VPP,以及其他控制量。不足及优化:
1.除了按键是用外部中断,其他的一切都是在主函数完成的,包括adc采集,数据处理,液晶显示。下一步可能会学一学dma,如果有时间就优化一下。
2.没有用到触发电平的方法整理波形,而是用根据上一次的初始电压,通过相位比较方法防止波形移动的,所以对方波,pwm信号的显示效果非常差。。。为什么不设置触发电平呢?按键少,嫌麻烦(可给我牛逼坏了,插会儿腰)。。。然而这是一定要加的,不加就不是完整的示波器了!
3.采样率达不到理论最大值(约1M)。
4.刷新频率太低,眼睛会看到屏幕闪烁。
问题:
1.我通过公式里的(1.5+12.5)*adc周期计算的采样频率是14/12us 即1.17us,但实际测量得到的结果是4.5us,是哪些地方拖慢了采样率呢?串口液晶屏?数据处理?
2.我刷新屏幕的频率大约是每秒两次,用的是LCD_Clear(BLACK); 一闪一闪的,有没有优雅一点的方法?
3.我现在的采样率是1/4.5us=222KHz,根据采样定律,我可以采集100KHz以内的波形,该怎么做呢?能不能给个思路呢?
先贴几张图
算了网不太好贴不上来。。。
下面是adc初始化代码
[mw_shl_code=cpp,true]void Adc_Init(void)
{
ADC_InitTypeDef ADC_InitStructure;
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA |RCC_APB2Periph_ADC1 , ENABLE ); //使能ADC1通道时钟
RCC_ADCCLKConfig(RCC_PCLK2_Div6); //设置ADC分频因子6 72M/6=12,ADC最大时间不能超过14M
//PA1 作为模拟通道输入引脚
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN; //模拟输入引脚
GPIO_Init(GPIOA, &GPIO_InitStructure);
ADC_DeInit(ADC1); //复位ADC1,将外设 ADC1 的全部寄存器重设为缺省值
ADC_InitStructure.ADC_Mode = ADC_Mode_Independent; //ADC工作模式:ADC1和ADC2工作在独立模式
ADC_InitStructure.ADC_ScanConvMode = DISABLE; //模数转换工作在单通道模式
ADC_InitStructure.ADC_ContinuousConvMode = DISABLE; //模数转换工作在单次转换模式
ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None; //转换由软件而不是外部触发启动
ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right; //ADC数据右对齐
ADC_InitStructure.ADC_NbrOfChannel = 1; //顺序进行规则转换的ADC通道的数目
ADC_Init(ADC1, &ADC_InitStructure); //根据ADC_InitStruct中指定的参数初始化外设ADCx的寄存器
ADC_Cmd(ADC1, ENABLE); //使能指定的ADC1
ADC_ResetCalibration(ADC1); //使能复位校准
while(ADC_GetResetCalibrationStatus(ADC1)); //等待复位校准结束
ADC_StartCalibration(ADC1); //开启AD校准
while(ADC_GetCalibrationStatus(ADC1)); //等待校准结束
// ADC_SoftwareStartConvCmd(ADC1, ENABLE); //使能指定的ADC1的软件转换启动功能
} [/mw_shl_code]
下面是庞大的main.c
[mw_shl_code=cpp,true]#include "led.h"
#include "delay.h"
#include "sys.h"
#include "usart.h"
#include "lcd.h"
#include "adc.h"
#include "exti.h"
//ALIENTEK Mini STM32开发板范例代码15
//ADC实验
//技术支持:
www.openedv.com
//广州市星翼电子科技有限公司
int time=0; //为了降低采样率而加延时
u8 sharp=1; //为了将横向拉长
float high=1; //为了纵向缩放
u16 buf[10]={0}; //用作缓冲数据
u16 adc[500]; //装载要绘制的波形
float frequency=0;
u8 mode=0;
void drawgaid() //画网格
{
u16 x,y;
for(x=0;x<201;x=x+25)
for(y=0;y<251;y=y+5)
{
LCD_Fast_DrawPoint(x,y,YELLOW );
}
for(y=0;y<251;y=y+25)
for(x=0;x<201;x=x+5)
{
LCD_Fast_DrawPoint(x,y,YELLOW );
}
}
float get_Vppandfrequency() //看数组,找max,min,时间差,算VPP,周期
{
int adcmax=0;
int adcmin=0;
float f=0;
int i=0,maxlocation=0,minlocation=0;
float VPP;
for(i=0,adcmax=adc[0],adcmin=adc[0];i<500;i++)
{
if(adc
>adcmax+10) adcmax=adc;
if(adc<adcmin-10) adcmin=adc;
}
VPP=(adcmax-adcmin)*(3.3/4096);
f=abs(1000000/((maxlocation-minlocation)*((200+time)/25))/2);
if(f!=0) frequency=f;
return VPP;
}
int findbegin() //通过比较与上一的波形比较,找到该从1何处开始画
{
int i,a;
for(i=0,a=0;i<250&&a<10;)
{
if(adc[i+a]>buf[a]-50&&adc[i+a]<buf[a]+50) a++;//符合:向下比较
else i++,a=0; //不符合:i++,a置零,重比
}
return i;
}
void drawwave(int begin) //从begin开始画200/sharp个点,到[0;sharp;200]
{
int i=0;
u16 x=0,y=0,temp=0,x0=0,y0=0;
POINT_COLOR=BLUE;//设置字体为蓝 {MOD}
for(i=begin;i<begin+200/sharp;i++)
{
x=(i-begin)*sharp;
y=(u16)(125-(125*adc/4096)/high);
LCD_Fast_DrawPoint(x,y,BLUE );
if(i!=begin) LCD_DrawLine(x0,y0,x,y);
x0=x,y0=y;
}
}
int main(void)
{
int i=0,a=0;
int begin=0; //找该从哪里描点
float VPP;
char length[50];
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);// 设置中断优先级分组2
delay_init(); //延时函数初始化
uart_init(9600); //串口初始化为9600
EXTIX_Init();
LED_Init(); //初始化与LED连接的硬件接口
LCD_Init();
Adc_Init(); //ADC初始化
LCD_Clear(BLACK);
drawgaid();
while(1)
{
adc[i++]=Get_Adc(ADC_Channel_1); //填入数组
if(time>0) delay_us(time);
if(i==500)
{
begin=findbegin();
LCD_Clear(BLACK);
drawgaid();
drawwave(begin);
VPP=get_Vppandfrequency(begin);
POINT_COLOR=BLUE;//设置字体为蓝 {MOD}
sprintf((char*)length,"VPP=%4.4lfV",VPP); //为了VCC数值输出
LCD_ShowString(0,260,200,12,12,length);
sprintf((char*)length,"frequency=%4.4lfHZ",frequency);
LCD_ShowString(100,260,200,12,12,length);
sprintf((char*)length,"time=%4d",time);
LCD_ShowString(0,280,200,12,12,length);
sprintf((char*)length,"sharp=%4d",sharp);
LCD_ShowString(100,280,200,12,12,length);
sprintf((char*)length,"mode=%d",mode);
LCD_ShowString(0,300,200,12,12,length);
sprintf((char*)length,"timeperiod=%4.4lfus",(4.5+(float)time)*25/sharp);//通过实验强行推出采样率,得到每格的时间
LCD_ShowString(0,240,200,12,12,length);
sprintf((char*)length,"Vperiod=%4.4lfV",3.3/5/high);//每纵格代表的电压
LCD_ShowString(0,220,200,12,12,length);
for(a=0;a<10;a++)
{
buf[a]=adc[begin+a]; //将本次输入点存档
}
i=0;
LED0=!LED0;
delay_ms(250);
}
}
}
[/mw_shl_code]
一周热门 更多>