stm32zet6自制简易示波器

自制简易示波器

波形发生

DAC+DMA+TMR
波形发生器采用的是DAC+DMA+TMR的方案,主要思路:PA4 DAC的通道1,通过DMA把内存dataDAC[N]中的数据传送到DAC,并转换成模拟量输出。
配置直接看代码吧,tmr6 作为触发这个参考手册上都有网上资源也多,我就不废话了。
看代码:

/*-------------------------------------------------------------------------------------
  mini示波器
	2021.2.18 bySDS	
	 TMR+DMA+DAC实现	 
	DAC  使能PA4	
---------------------------------------------------------------------------------------*/
#include "dac.h"
void Dac1_Init(void)
{
	DAC_InitTypeDef DACtypeDef;
	GPIO_PortClock(GPIOA,ENABLE); //端口A 使能
	GPIO_PinConfigure(GPIOA,4,GPIO_IN_ANALOG,GPIO_MODE_INPUT);  //端口4的模式配置
  RCC_APB1PeriphClockCmd(RCC_APB1Periph_DAC,ENABLE);	
	DAC_DeInit(); //
	DAC_StructInit(&DACtypeDef);//
	DACtypeDef.DAC_LFSRUnmask_TriangleAmplitude=DAC_LFSRUnmask_Bit0;//不使用自带的就不需要
	DACtypeDef.DAC_OutputBuffer=DAC_OutputBuffer_Disable;
	DACtypeDef.DAC_Trigger=DAC_Trigger_T6_TRGO;
	DACtypeDef.DAC_WaveGeneration=DAC_WaveGeneration_None;
	DAC_Init(DAC_Channel_1,&DACtypeDef);
		DAC_Cmd(DAC_Channel_1,ENABLE);
	DAC_DMACmd(DAC_Channel_1,ENABLE);
}

使用基本定时器来触发转换
DMA配置:

void MYDMA2_Config(DMA_Channel_TypeDef* DMA_CHx,u32 cpar,u32 cmar,u16 cndtr)
{
	 DMA_InitTypeDef DMAtypeDef;
	RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA2,ENABLE);
	 DMA_StructInit(&DMAtypeDef);
	  DMA_DeInit(DMA_CHx);
	//基本配置,都不用咋说了,资料上多
	  DMAtypeDef.DMA_BufferSize=cndtr;
	DMAtypeDef.DMA_DIR=DMA_DIR_PeripheralDST;//外设作为目的,即把内存中的数据传到dac外设
	DMAtypeDef.DMA_M2M=DMA_M2M_Disable;
	DMAtypeDef.DMA_MemoryBaseAddr=cmar;
	DMAtypeDef.DMA_MemoryDataSize=DMA_MemoryDataSize_HalfWord;
	DMAtypeDef.DMA_MemoryInc=DMA_MemoryInc_Enable;//内存地址递增,内存有len长度大小
	DMAtypeDef.DMA_Mode=DMA_Mode_Circular;//循环模式
	DMAtypeDef.DMA_PeripheralBaseAddr=cpar;
	DMAtypeDef.DMA_PeripheralDataSize=DMA_PeripheralDataSize_HalfWord;//半字16bit
	DMAtypeDef.DMA_PeripheralInc=DMA_PeripheralInc_Disable;
	DMAtypeDef.DMA_Priority=DMA_Priority_Low;//优先级设置
	 
	DMA_Init(DMA_CHx,&DMAtypeDef);
	 DMA_Cmd(DMA_CHx,ENABLE);
}

波形函数的写法,这里只用三角函数为例:

        for (i = 0; i < len; i++)
        {
            dataDAC[i] = (u16)(base + sin((double)i / len * 2*pi) * amp);
        }

DAC定时器数据 TIM6
rcc:499,pre: 71 Tout=500*72/72 =500us, 默认 2KHZ
256个点每500us

示波器 代码

采用的是ADC+DMA+TMR。过程:TMR触发adc转换,dma通过将外设adc上的数据采集到内存里。直接看代码:

/**********************************************************
					使用ADC1 CH6 做ADC入口检测
***********************************************************/ 
 #include "adc.h"
 
void Adc_Init(void)
{
		ADC_InitTypeDef ADCtypeDef;
		GPIO_PortClock(GPIOA,ENABLE);
		RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1,ENABLE);
	  		ADC_DeInit(ADC1);
		RCC_ADCCLKConfig(RCC_PCLK2_Div6);
		ADC_RegularChannelConfig(ADC1, ADC_Channel_6, 1, ADC_SampleTime_13Cycles5);//    1/12 *(12.5+13.5)  =2.17us,时间长adc精度高

		GPIO_PinConfigure(GPIOA,6,GPIO_IN_ANALOG,GPIO_MODE_INPUT);//72/6=12MHz	

	ADCtypeDef.ADC_ContinuousConvMode=DISABLE;
	ADCtypeDef.ADC_DataAlign=ADC_DataAlign_Right;
	ADCtypeDef.ADC_ExternalTrigConv=ADC_ExternalTrigConv_T3_TRGO;//类似
	ADCtypeDef.ADC_Mode=ADC_Mode_Independent;
	ADCtypeDef.ADC_NbrOfChannel=1;
	ADCtypeDef.ADC_ScanConvMode=DISABLE;
		ADC_Init(ADC1,&ADCtypeDef);
	
	ADC_Cmd(ADC1,ENABLE);
	ADC_DMACmd(ADC1,ENABLE);
	
	ADC_ResetCalibration(ADC1);
	while(ADC_GetResetCalibrationStatus(ADC1));
	ADC_StartCalibration(ADC1);
	while(ADC_GetCalibrationStatus(ADC1));
	ADC_ExternalTrigConvCmd(ADC1,ENABLE);
}

DMA中设置:基本和上述类似,只不过这个是外设为源。


void MYDMA1_Config(DMA_Channel_TypeDef* DMA_CHx,u32 cpar,u32 cmar,u16 cndtr)
{
	 DMA_InitTypeDef DMAtypeDef;
	
	RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1,ENABLE);
	 DMA_StructInit(&DMAtypeDef);
	  DMA_DeInit(DMA_CHx);
	
	  DMAtypeDef.DMA_BufferSize=cndtr;
	DMAtypeDef.DMA_DIR=DMA_DIR_PeripheralSRC;
	DMAtypeDef.DMA_M2M=DMA_M2M_Disable;
	DMAtypeDef.DMA_MemoryBaseAddr=cmar;
	DMAtypeDef.DMA_MemoryDataSize=DMA_MemoryDataSize_HalfWord;
	DMAtypeDef.DMA_MemoryInc=DMA_MemoryInc_Enable;
	DMAtypeDef.DMA_Mode=DMA_Mode_Circular;
	DMAtypeDef.DMA_PeripheralBaseAddr=cpar;
	DMAtypeDef.DMA_PeripheralDataSize=DMA_PeripheralDataSize_HalfWord;
	DMAtypeDef.DMA_PeripheralInc=DMA_PeripheralInc_Disable;
	DMAtypeDef.DMA_Priority=DMA_Priority_High;
	 
	DMA_Init(DMA_CHx,&DMAtypeDef);
		
	 DMA_Cmd(DMA_CHx,ENABLE);
}

TMR也类似:

/*
  定时器3 关于ADC1 的触发转换
:	Tout=((arr+1)*(psc+1))/Ft us.
*/
void TIM3_Int_Init(u16 arr,u16 psc)
{  
	TIM_TimeBaseInitTypeDef  TIM_BaseTypeDef;

 	GPIO_PortClock(GPIOA,ENABLE);	
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);

		TIM_DeInit(TIM3);
	
	TIM_BaseTypeDef.TIM_ClockDivision=TIM_CKD_DIV1;
	TIM_BaseTypeDef.TIM_CounterMode=TIM_CounterMode_Up;
	TIM_BaseTypeDef.TIM_Period=arr;
	TIM_BaseTypeDef.TIM_Prescaler=psc;
	TIM_BaseTypeDef.TIM_RepetitionCounter=0;
		TIM_TimeBaseInit(TIM3,&TIM_BaseTypeDef);
	
	TIM_SelectOutputTrigger(TIM3,TIM_TRGOSource_Update);
}

ADC 转换时间		1/12 *(12.5+13.5)=2.17us 
定时器数据   TIM3
rcc:20, pec:1800(ret) Fout =72/20/ret 初始情况 : Fout=2KHz  500us
502.17us      1995 Hz    每隔502.17us  发生ADC转换
分辨率995/256=7.79.

fft

cr4_fft_256_stm32(lBufOutArray,lBufInArray, NTP);//官方的库,用的256点
signed int  lBufInArray[NTP];    
signed int lBufOutArray[NTP/2]; 
signed int lBufMagArray[NTP/2]; 

 
void GetPowerMag(void)
{              
    float X,Y,Mag;                                                      
    unsigned short i;
    for(i=0; i<NTP/2; i++)                        //FFT  A0=lBufOutArray[0]/NPT
    {                                             //   Ai=lBufOutArray[i]*2/NPT
        X  = (lBufOutArray[i] << 16) >> 16;  //lX  = lBufOutArray[i];
        Y  = (lBufOutArray[i] >> 16);
        Mag = sqrt(X * X + Y * Y);
        if(i == 0)
            lBufMagArray[i] = (Mag);   //
        else
            lBufMagArray[i] = (Mag*2);
    }
}

void adc_sample(void)
{
	u16 i=0;
	memset(lBufMagArray,0,NTP/2);
	memset(lBufOutArray,0,NTP/2);
	memset(lBufInArray,0,NTP);
    for (i = 0; i < NTP; i++)
    {
        lBufInArray[i] = dataADC[i];
    }
    cr4_fft_256_stm32(lBufOutArray,lBufInArray, NTP);
    GetPowerMag();
}

lBufMagArray:存放幅值的(正)。频率分辨率*最大的幅值对应的坐标

菜单代码

为了做得像样一点,添加了菜单进去,整个程序只用到了3个按键:wkup(确定返回),key0(down),key1(up)精英版上的按键

typedef struct
{
	u8 curTask;
	u8 up;// 				/KEY1
	u8 down;//      //KEY0
	u8 enter;//       //WKUP 
	void (*current_operation)();
} key_table;

key_table  table[]=        //多级菜单
{                        //      1     2      3     1
	{0,		0,	0,	1,startUI}, //过程 ADC-->DATA-->DAC-->ADC
	{1,		3,	2,	4,showMenu1},   //1: ADC
	{2,		1,	3,	5,showMenu2},   //2: DATA
	{3,		2,	1,	6,showMenu3},   //3:DAC
	
	{4,		4,	4,	7,showADCWaveUI},
	{5,		5,	5,	8,showDataUI},
	{6,		13,	10,	9,showDACWaveUI},
	
	{7,		7,	7,	1,doADCWave},
	{8,		8,	8,	2,doDataPre},
	{9,		9,	9,	3,doDACWave},
	
	{10,6,11,9,showDACWaveUI2},
	{11,10,12,9,showDACWaveUI3},
	{12,11,13,9,showDACWaveUI4},
	{13,12,6,9,showDACWaveUI5},
};

主函数:

int main(void)
 {
	 u8 key;
	 systemInit();   
		 while(1)
		 {
			 key=KEY_Scan(0);
			 if(key&&(OS_TASK==RUN_TASK||OS_TASK==ADC_TASK||OS_TASK==DAC_TASK||OS_TASK==DATA_TASK))
			 {
					LED1=!LED1;
					switch(key)
					 {
						case KEY1_PRES: fun_index=table[fun_index].up;break;
						case KEY0_PRES: fun_index=table[fun_index].down;break;
						case WKUP_PRES:	fun_index=table[fun_index].enter;break;				 
					 }					 
							current_operation_index=table[fun_index].current_operation;
			 }
				current_operation_index();															
		 }
} 

主函数整个过程其实就是,循环执行current_operation_index(),函数指针被不停的重新赋值。

只执行一次的fun

形如:

void showMenu3()
{
	OLED_CLS();
	OLED_ShowStr(30,24, (u8 *)"DAC",24,1);
	OLED_ShowStr(30,0,	(u8 *)"UP  :DAT",12,1);
	OLED_ShowStr(30,48, (u8 *)"DOWN:ADC",12,1);
	OLED_Refresh_Gram();
}

循环执行的fun

其实加了while和状态标志判断,这样可以实现在fun内的循环了,此时wkup才能退出,而key0和key1就可以用作其他用途。

void doADCWave()
{
	u8 key;
	OS_TASK=ADC_TASK;
	TIM_Cmd(TIM3,ENABLE);
	while(OS_TASK==ADC_TASK)
	{
			key = KEY_Scan(0);
			if(key)  
			{
				LED1=~LED1;
				switch(key)
				{
					case KEY0_PRES:ret+=100;
													ret= ret> 65535 ? 65535:ret;
												 TIM_PrescalerConfig(TIM3,ret-1,TIM_PSCReloadMode_Update);break;    //向下翻
					case KEY1_PRES:	ret-=100;
													 ret= ret< 1 ? 1:ret;
													TIM_PrescalerConfig(TIM3,ret-1,TIM_PSCReloadMode_Update);break;   	//向上翻
					case WKUP_PRES:OS_TASK=RUN_TASK;//退出循环	
										fun_index=table[fun_index].enter;									
										current_operation_index=table[fun_index].current_operation;	
										break;             
				}		
			}
				doShowWave(dataADC);
		}
	  TIM_Cmd(TIM3,DISABLE);
}

简易示波器

OLED为IIC模式,传输速率好像有点拉跨,屏幕帧率太低的
本人能力有限,错误的地方非常感谢指出,整个工程在这里:
https://download.csdn.net/download/qq_43530159/15512913

你可能感兴趣的:(自制,单片机,c语言)