本人初学stm32,为了加深对数据采集 数据存储 ADC DAC DMA的操作!本人在2011年的电子大赛中找到了一个题《《波形的采集存储和回放》》,决定拿下它,刚开始对STM32一点都不熟练,一些简单的配置的不会,特别是在ADC采样DMA配置中吃了很多苦头,循环缓存模式和Normal模式的选择时,我第一次就选择的是循环缓存模式,我在SRAM中定义了个100个字节大小的数组,我就想确定我采集的数据是不是和原始波形的数据一样,我就把数组里的数据通过在液晶上画点画出来的,最后我发现波形很乱,我就判断我采集数据不对!为啥不对呢?配置那些都是对的呀,突然想到我用的是循环缓存模式,循环模式是在数据把数组里装满后我们还没有来得及去存储它,新来的数据又把数组上次采集的数据覆盖了,最后我换成了Normal模式采集了1200个字节的数据,在液晶上画点波形很给力,这下我的数据对了!我又开始整存储,我采取的方法是把它存在内部FLASH,用它来模拟一个EEPROM,因为我们的数据很小所以可以存在他的内部FLASH里面,我是从内部地址#define FLASH_SAVE_ADDR 0X08070000开始存储,在写入FLASH时注意他是有写保护的,在写入时对FLASH解锁,最后数据写入完成时必须给FLASH上锁!!FLASH里面的操作是很难的,向我们这些新手需要多花时间,到时候需要代码的可以找我要,我希望我在电子世界里交更多的好朋友,做技术是很寂寞的!!我采取按键的方法写入和读出,读出的数据我存在SRAM定义的一个数组中通过DMA把它发送到这个DAC数据保持寄存器 DAC_DHR12R1 地址为#define DAC_DHR12R1_Address ((u32)0x40007408) 我还用用这个数组里面的数据在我的液晶上画了波形图,看起来还不错 最后我才去示波器的2个通道,一个监视原始波形,一个监视从FLASH的读回来的波形,周期和原来的波形的周期在误差范围类!! 我采样的是示波器上面的方波电压在0~2.5V 下面发几张图给大家看看 部分代码 要要完整的联系我的QQ:554253547 希望多认识朋友 交流技术 我下面想做个GPS,希望有人能提供技术支持!!!!!里面的那个板子是自己画的 感觉有点丑 但是是自己的处女作!!!!
#include "led.h"
#include "stmflash.h"
#include "delay.h"
#include "sys.h"
#include "timer.h"
#include "lcd.h"
#include "adc.h"
#include "dma.h"
#include "usart.h"
#include "key.h"
#include "dac.h"
u16 ADC_ConvertedValue[600];//ADC采样回来存储的数组
//u16 ADC_ConvertedValue[50];//ADC采样回来存储的数组
//extern u8 TIM5CH1_CAPTURE_STA; //输入捕获状态
//extern u16 TIM5CH1_CAPTURE_VAL; //输入捕获值
u16 datatemp[600];//从芯片内部FLASH里面读回的ADC采样值
#define FLASH_SAVE_ADDR 0X08070000 //设置FLASH 保存地址(必须为偶数)
/************************************************************************
****************** MAIN函数 ************************
************************************************************************/
int main(void)
{
u16 i=0; //循环次数
u8 key; //扫描的按键值
u16 adcx; //从FLASH读出的AD采样
float temp; // 采集的每个点对应的电压值
u16 x=0; //X轴代表的是时间
u16 y; //Y轴上对应的电压大小
u16 Y_VALUE; // Y的值
u16 X_VALUE; // X的值
u16 X_LAST_VALUE; //上次X的值
u16 Y_LAST_VALUE; //上次Y的值
SystemInit();
delay_init(72); //延时初始化
NVIC_Configuration();
uart_init(9600);
LED_Init(); //LED初始化
LCD_Init(); //液晶初始化
Adc_Init(); //ADC初始化
TIM5_Cap_Init(0XFFFF,71); //以1Mhz的频率计数
Dac1_Init(); //DAC初始化
DMA_ADC_Config((u32)ADC_ConvertedValue); //运输ADC采样值到SRAM的DMA
DMA_DAC_Config((u32)datatemp); //运输FLASH内存储的ADC采样值到DAC的DMA
POINT_COLOR=RED;
LCD_ShowString(60,10,"Peter ILOVEpu");
LCD_ShowString(60,30,"PA0_round_T:1Ms"); //显示周期
LCD_ShowString(60,50,"MAX_U: 2.5V"); //最大值
LCD_ShowString(60,70,"MIN_U: 0V"); //最小值
// LCD_ShowString(60,70,"PA1_Round: us");
while(1)
{
//
/************************************************************************
****************** 捕获周期函数 ************************
************************************************************************/
// delay_ms(10);
//
// POINT_COLOR=BLUE;
// if(TIM5CH1_CAPTURE_STA&0X80)//成功捕获到了一次上升沿
// {
// temp=TIM5CH1_CAPTURE_STA&0X3F; // 去掉bit7和bit6 只剩下0~5位为我们所需溢出次数
// temp*=65536;//溢出时间总和
// temp+=TIM5CH1_CAPTURE_VAL;//得到总的高电平时间
// round=2*temp;
// LCD_ShowNum(140,70,round,4,16);//打印总的高点平时间
// TIM5CH1_CAPTURE_STA=0;//开启下一次捕获
// }
////
// }
/************************************************************************
****************** FLASH测试函数 ************************
************************************************************************/
// for(i=0;i<600;i++)
// {
// DAC_SetChannel1Data(DAC_Align_12b_R,datatemp); //12位右对齐数据格式设置DAC值
// key=KEY_Scan(0);
//
// if(key==1)//KEY0按下,写入STM32 FLASH
// {
//
// LED0=!LED0;
// //LCD_Fill(0,150,239,319,WHITE);//清除半屏
// delay_ms(300);
// LCD_ShowString(60,190,"Start Write FLASH...");
// STMFLASH_Write(FLASH_SAVE_ADDR,(u16*)ADC_ConvertedValue,600);
// LCD_ShowString(60,210,"FLASH Write Finished!"); //提示传送完成
//
// }
// if(key==2)//KEY1按下,读取FLASH内部的数据
// {
// delay_ms(300);
// LCD_ShowString(60,70,"Start Read FLASH....");
// STMFLASH_Read(FLASH_SAVE_ADDR,(u16*)datatemp,600);
// LCD_ShowString(60,90,"FLASH Read Finished!"); //提示传送完成
// // i=0;
// LED1=!LED1;
//
//
// }
// adcx=datatemp;
// LCD_ShowNum(156,30,adcx,4,16);//显示ADC的值
// temp=(float)adcx*(3.3/4096);
//
//
// adcx=temp;
// LCD_ShowNum(156,50,adcx,1,16);//显示电压值
// temp-=adcx;
// temp*=1000;
// LCD_ShowNum(172,50,temp,3,16);
//// LED1=!LED1;
// }
/************************************************************************
****************** 示波器及回放 ************************
************************************************************************/
for(i=0;i<600;i++)
{
delay_ms(10);
POINT_COLOR=BLUE;
adcx=datatemp;
key=KEY_Scan(0);
if(key==1)//KEY0按下,写入STM32 FLASH
{
LED0=!LED0;
LCD_Fill(0,150,239,319,WHITE);//清除半屏
delay_ms(500);
LCD_ShowString(60,90,"Start Write FLASH...");
STMFLASH_Write(FLASH_SAVE_ADDR,(u16*)ADC_ConvertedValue,600);
LCD_ShowString(60,110,"FLASH Write Finished!"); //提示传送完成
}
if(key==2)//KEY1按下,读取FLASH内部的数据
{
delay_ms(500);
LCD_ShowString(60,90,"Start Read FLASH....");
STMFLASH_Read(FLASH_SAVE_ADDR,(u16*)datatemp,600);
LCD_ShowString(60,110,"FLASH Read Finished!"); //提示传送完成
LED1=!LED1;
// i=0;
}
// LCD_ShowNum(156,30,adcx,4,16);//显示ADC的值
temp=(float)adcx*(3.3/4096);
Y_LAST_VALUE=Y_VALUE; //取上一次的y值
y=220-temp*24; //确定y的值
Y_VALUE=y;
adcx=temp;
// LCD_ShowNum(156,50,adcx,1,16);//显示电压值
temp-=adcx;
temp*=1000;
// LCD_ShowNum(172,50,temp,3,16);
POINT_COLOR=BLUE;
LCD_DrawPoint(x,y); //画点
X_LAST_VALUE=X_VALUE; //取上一次的x值
x++;
X_VALUE=x;
if(x==240)
{
x=0;
LCD_Fill(0,150,240,320,WHITE); //清屏
}
if(Y_LAST_VALUE!=0&&x!=0&&X_LAST_VALUE!=240&&X_LAST_VALUE!=0)
LCD_DrawLine(X_LAST_VALUE,Y_LAST_VALUE,x,y); //画线
LED1=!LED1;
}
}
}
#include "dma.h"
#define DAC_DHR12R1_Address ((u32)0x40007408)//DAC数据保持寄存器地址
#define ADC1_DR_Address ((u32)0x4001244c)//ADC数据采集地址
/****************************************************************************
DMA ADC采样的数据转移到SRAM中的ADC_ConvertedValue[]数组中
ADC采样转换的数据寄存器地址 ADC1_DR_Address=((u32)0x4001244c)
*****************************************************************************/
void DMA_ADC_Config(u32 cmar)
{
DMA_InitTypeDef DMA_InitStruct;
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE); //使能DMA传输
DMA_DeInit(DMA1_Channel1); //复位DMA1的第一通道,关闭状态设置
DMA_InitStruct.DMA_PeripheralBaseAddr = ADC1_DR_Address; //DMA对应的外设基地址
DMA_InitStruct.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord; //转换结果的数据大小
DMA_InitStruct.DMA_MemoryBaseAddr = cmar; //内存地址
DMA_InitStruct.DMA_DIR = DMA_DIR_PeripheralSRC; //DMA的转换模式是SRC模式,就是从外设向内存中搬运
DMA_InitStruct.DMA_M2M = DMA_M2M_Disable; //M2M模式禁止,memory to memory
DMA_InitStruct.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord; //DMA搬运的数据尺寸,注意ADC是12位的,
//接收一次数据后,目标内存地址后移,用来采集多个数据的
DMA_InitStruct.DMA_MemoryInc = DMA_MemoryInc_Enable;
DMA_InitStruct.DMA_PeripheralInc = DMA_PeripheralInc_Disable; //接收一次数据后,设备地址不后移
//转换模式:常用循环缓存模式。如果M2M开启了,则这个模式失效
//另一种是Normal模式:不循环,仅一次DMA
DMA_InitStruct.DMA_Mode = DMA_Mode_Normal;
DMA_InitStruct.DMA_Priority = DMA_Priority_High; //DMA优先级,高
DMA_InitStruct.DMA_BufferSize = 50; //DMA缓存大小
DMA_Init(DMA1_Channel1,&DMA_InitStruct);
// Enable DMA1
// DMA_Cmd(DMA1_Channel1, ENABLE);
ADC_SoftwareStartConvCmd(ADC1, ENABLE);
DMA_Cmd(DMA1_Channel1, ENABLE);
}
/*********************************************************************************
DMA SRAM的数据转换到DAC数据保持寄存器
数据保持寄存器的地址为 DAC_DHR12R1_Address=0x40007408
**********************************************************************************/
void DMA_DAC_Config(u32 cmar)
{
DMA_InitTypeDef DMA_InitStruct;
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE); //使能DMA传输
DMA_DeInit(DMA1_Channel1); //复位DMA1的第一通道,关闭状态设置
DMA_InitStruct.DMA_PeripheralBaseAddr = DAC_DHR12R1_Address; //DMA对应的外设基地址
DMA_InitStruct.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord; //转换结果的数据大小
DMA_InitStruct.DMA_MemoryBaseAddr = cmar; //内存地址
DMA_InitStruct.DMA_DIR = DMA_DIR_PeripheralDST; //DMA的转换模式是DST模式,就是从内存向外设中搬运
DMA_InitStruct.DMA_M2M = DMA_M2M_Disable; //M2M模式禁止,memory to memory
DMA_InitStruct.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord; //DMA搬运的数据尺寸,注意DAC是12位的,
//发送一次数据后,目标内存地址后移,用来发送多个数据的
DMA_InitStruct.DMA_MemoryInc = DMA_MemoryInc_Enable;
DMA_InitStruct.DMA_PeripheralInc = DMA_PeripheralInc_Disable; //接收一次数据后,设备地址不后移
//转换模式:常用循环缓存模式。如果M2M开启了,则这个模式失效
//另一种是Normal模式:不循环,仅一次DMA
DMA_InitStruct.DMA_Mode = DMA_Mode_Circular;
DMA_InitStruct.DMA_Priority = DMA_Priority_High; //DMA优先级,高
DMA_InitStruct.DMA_BufferSize = 600; //DMA缓存大小
DMA_Init(DMA1_Channel1,&DMA_InitStruct);
// Enable DMA1
DMA_Cmd(DMA1_Channel1, ENABLE);
}
#include "dac.h"
/*********************************************************************************
DAC通道1输出初始化
**********************************************************************************/
void Dac1_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
DAC_InitTypeDef DAC_InitType;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE ); //使能PORTA通道时钟
RCC_APB1PeriphClockCmd(RCC_APB1Periph_DAC, ENABLE ); //使能DAC通道时钟
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4; // 端口配置
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN; //模拟输入
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
GPIO_SetBits(GPIOA,GPIO_Pin_4);//PA.4 输出高
DAC_InitType.DAC_Trigger=DAC_Trigger_None; //不使用触发功能 TEN1=0
DAC_InitType.DAC_WaveGeneration=DAC_WaveGeneration_None;//不使用波形发生
DAC_InitType.DAC_LFSRUnmask_TriangleAmplitude=DAC_LFSRUnmask_Bit0;//屏蔽、幅值设置
DAC_InitType.DAC_OutputBuffer=DAC_OutputBuffer_Disable ; //DAC1输出缓存关闭 BOFF1=1
DAC_Init(DAC_Channel_1,&DAC_InitType); //初始化DAC通道1
DAC_Cmd(DAC_Channel_1, ENABLE); //使能DAC1
DAC_SetChannel1Data(DAC_Align_12b_R, 0); //12位右对齐数据格式设置DAC值
}
//设置通道1输出电压
//vol:0~3300,代表0~3.3V
void Dac1_Set_Vol(u16 vol)
{
float temp=vol;
temp/=1000;
temp=temp*4096/3.3;
DAC_SetChannel1Data(DAC_Align_12b_R,temp);//12位右对齐数据格式设置DAC值
}
#include "adc.h"
//初始化ADC
//这里我们仅以规则通道为例
//我们默认将开启通道0~3
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); //72M/6=12,ADC最大时间不能超过14M
//PA0/1/2/3 作为模拟通道输入引脚
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;//|GPIO_Pin_1;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN; //模拟输入引脚
GPIO_Init(GPIOA, &GPIO_InitStructure);
ADC_DeInit(ADC1); //将外设 ADC1 的全部寄存器重设为缺省值
ADC_InitStructure.ADC_Mode = ADC_Mode_Independent; //独立模式
ADC_InitStructure.ADC_ScanConvMode = ENABLE; // 工作扫描模式
ADC_InitStructure.ADC_ContinuousConvMode =ENABLE;//模数转换工作在连续转换模式
ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None; //转换由软件而不是外部触发启动
ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;//转换的数据右对齐
ADC_InitStructure.ADC_NbrOfChannel = 1; //进行规则转换的ADC通道的数目
ADC_Init(ADC1, &ADC_InitStructure); //初始化各个寄存器
/* ADC1 regular channel_8 configuration */
ADC_RegularChannelConfig(ADC1, ADC_Channel_0, 1, ADC_SampleTime_239Cycles5); //PA0的采样的240周期
// ADC_RegularChannelConfig(ADC1, ADC_Channel_1, 2, ADC_SampleTime_239Cycles5); //PA1的采样的240周期
// ADC1 to DMA, Enable
ADC_DMACmd(ADC1, ENABLE); //ADC命令,和DMA关联
/* Enable ADC1 */
ADC_Cmd(ADC1, ENABLE); //使能指定的ADC1 ADC_CR2_ADON[0]=1; 打开A/D转换器
/* Enable ADC1 reset calibaration register */
ADC_ResetCalibration(ADC1); //重置指定的ADC1的校准寄存器 ADC1_CR2[3]=1;
/* Check the end of ADC1 reset calibration register */
while(ADC_GetResetCalibrationStatus(ADC1)); ////获取ADC1重置校准寄存器的状态,设置状态则等待
/* Start ADC1 calibaration */
ADC_StartCalibration(ADC1); //开始指定ADC1的校准状态 ADC1_CR2[2]=1;
/* Check the end of ADC1 calibration */
while(ADC_GetCalibrationStatus(ADC1)); //获取指定ADC1的校准程序,设置状态则等待
/* Start ADC1 Software Conversion */
}
|