|
今天是3月9号,再过3个月就要大学毕业了,最近工作找得郁闷。干脆躲回实验室,总结下我的毕业设计,算是把自己的大学画个句号吧。 * x/ j5 |: T" k4 a7 q % L( u: ?4 G: j. m- p4 k) T; | 1 概述 3 o; X* U' n9 F7 ~8 o. f6 X 毕业设计的题目是“基于STM32的简易示波器设备驱动的研究”,就是利用STM32的AD采集波形数据,然后通过它内置的USB2.0把数据传到PC上面显示出来。框图如下。 题目一共是两个人做,我和我搭档耗子。他做信号处理,采集部分。我做USB在PC下的驱动开发,以及上位机界面的显示。USB固件那部分我们是一起完成的。 这个课题从08年10月开始,到12月圣诞前夕基本完成。最后基本实现了波形的显示,实测最大可以测量的频率是200KHZ, 效果图如下: " I9 P1 E, c; l <font color="#000000">方波的图形好像不是很漂亮会有上升沿老是有尖刺,还需要作软件做处理。没截出来, 最后能出数据离不开很多前辈的经验。有些地方是借鉴了他们的东西。现在先列出来。 ( i6 _% ? P) l4 b8 F8 S 第一个是21IC上面alien2006原帖地址。他也是做了一个简易示波器,但是用的是以太网传输。采集部分我很多借鉴了他的方案。 8 w& H Z# s3 `) C% h- A) C c/ B 第二个是电脑圈圈,他对USB的理解令我钦佩。我能搞出驱动,他提供的源码包非常重要,有些代码也是直接在他的基础上修改的。 2 整体方案 先说下一次完整的采集,比如外面进来的波形是正弦波,波形电压有正负,STM32单片机的AD只能采集0~3.6V的电压,所以要对信号进行 处理。也就是需要一个模拟前端电路,把电压抬上去。接着就是采集了,STM32的AD可以用外设进行触发,这里用定时器进行触发。每过一个单位时间AD开 启一次,采集一个点,这样采集的频率只要调整这个单位时间也就是定时器就可以控制了。采集了一桢数据,比如200个点。DMA中断被触发,开启USB,把 数据发送到上位机,然后显示出来。 $ V$ W1 \: k( u) w+ ^) z 整个过程大体就是这样了,还有一个很重要的环节补充下。熟悉示波器的人都知道示波器有个触发概念。像刚才这样显示的话,比如前一帧数据是波 峰开始显示,后一帧是波谷开始。这样显示出来的波形就是乱的,于是为了解决这个问题,就需要做触发,也就是保证每次采集的起始电位相同。我们的采用的是用 外中断的形式,外面波形数据先不采集,先让它通过一个比较器,比如比较器的基准电压是1V,也就是每次都和1V比较,低于1V输出低电平,高于1V输出高 电平。当数据电压大于1V的时候,比较器输出高电平,高电平接到单片机外中断口,这样外中断就被触发。然后开始采集,这样就能保证每帧数据的起始点都相 同。 这个方案是大体方案,后来做了下修改。就是让AD一直在采集。外中断触发了后开启的是DMA,AD一直开着,只是控制DMA什么时候去取。从哪里开始取,取多少个点。下位机部分不再详说,贴下主要的程序,大侠们随便看看,多多指教。 ' k, P& v) m9 F( y3 c 定时器设置代码如下: ! G1 _9 u) S4 w X/ ~ /******************************************************************************* * Function Name : TIM_Configuration * Description : Configure the TIM2. *******************************************************************************/ 4 o8 }# ?+ W2 B/ D! O _ void TIM2_Configuration(void) { # Q0 i# I$ j: M% f4 T7 e: S8 [ TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure; TIM_OCInitTypeDef TIM_OCInitStructure; TIM_DeInit(TIM2); //TIM2初始化 " m0 m) F+ F7 y# C( H /* Time base configuration */ . b3 v7 i) n/ z6 w* p5 I, i TIM_TimeBaseStructure.TIM_Period = 18; //设置了下一个更新事件装入活动的自动重装载寄存器周期的值 TIM_TimeBaseStructure.TIM_Prescaler = 0; //设置了用来作为TIMx时钟频率除数的预分频值 $ W$ T4 ` M, g/ ? TIM_TimeBaseStructure.TIM_ClockDivision = 0; //设置了时钟分割 TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; //选择了计数器模式向上计数 1 j8 F6 o. @- m4 h7 u' J& D 4 E5 X, b$ g. @) S6 \ X TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure); //根据TIM_TimeBaseInitStruct中指定的参数初始化TIMx 5 H& A. M* D9 n3 L. p" d$ c& a3 z 9 ^& G# u" [9 C$ \9 v$ C6 H( o /* Prescaler configuration */ // TIM_PrescalerConfig(TIM2, 1, TIM_PSCReloadMode_Update); //设置TIMx重载次数 预分频值在更新事件装入 9 h2 q. |9 H* D : m G1 W( m+ }. C. w9 f4 r5 w /* TIM_OCMode_Toggle Mode configuration: Channel2 */ TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_Toggle; TIM_OCInitStructure.TIM_Channel = TIM_Channel_2; 4 u% ^6 ]3 ` }* w: R TIM_OCInitStructure.TIM_Pulse = 9; TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_Low; //TIM输出比较极性低 $ s& N; V4 O$ n1 n( d % m" e6 G) d' A" Y TIM_OCInit(TIM2, &TIM_OCInitStructure); 7 ]' `/ r9 }; h0 E& z TIM_ARRPreloadConfig(TIM2, ENABLE); //使能或者失能TIMx在ARR上的自动装载寄存器 * ]# e& s7 _3 g5 A) h* i- F2 ? /* TIM2 enable counter */ TIM_Cmd(TIM2, ENABLE); //使能TIMx外设 7 i) ~0 I' [% |# m. i7 p' C } AD以及DMA设置代码: /******************************************************************************* ; ~6 x- Z/ C% N, H b * Function Name : ADC_Configuration & H# N6 o2 ]" Y: I& B2 ?9 ~) l * Description : Configure the ADC1. *******************************************************************************/ void ADC_Configuration(void) { /* DMA channel1 configuration ----------------------------------------------*/ DMA_DeInit(DMA_Channel1); //复位DMA_Channel1 8 [1 C7 ~# f2 j/ M1 b$ u DMA_InitStructure.DMA_PeripheralBaseAddr = ADC1_DR_Address; //外围设备地址 DMA_InitStructure.DMA_MemoryBaseAddr = (u32)&ADC_Data[0]; //memory 地址 DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC; //外围设备做为源 ; n$ ^: `1 z! C DMA_InitStructure.DMA_BufferSize = 1024; //数据单元尺寸 2 @2 a9 N% F4 m7 S" ^5 a0 M DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; //外围地址是否自动增长 disable不增长 DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable; //memory 是否自动增长 1 J1 v# m+ e/ i! @. U DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord; //外围设备寄存器尺寸16位 . P/ C3 ]5 u5 f L+ g DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord; //memory尺寸16位 DMA_InitStructure.DMA_Mode = DMA_Mode_Circular; //DMA 循环模式 DMA_InitStructure.DMA_Priority = DMA_Priority_High; //DMA 通道优先级 DMA_InitStructure.DMA_M2M = DMA_M2M_Disable; //memory- to-memory转换 5 P/ N5 ] g8 E. o7 X3 h" r DMA_Init(DMA_Channel1, &DMA_InitStructure); //初始化DMA通道1 5 F9 Z, ?! O( u( c2 l* F : R! `+ U* t: y( R/ \6 w /* DMA IT enable */ 9 I2 Z! J" o6 q* U DMA_ITConfig(DMA_Channel1, DMA_IT_TC, ENABLE); //使能DMA传输完成中断 /* ADC1 configuration ------------------------------------------------------*/ 4 j# {/ \3 v/ i& O+ I ADC_InitStructure.ADC_Mode = ADC_Mode_Independent; //ADC 工作模式 ADC1 ADC2 单独工作 # @1 H& j7 K# y8 c" r" g1 d ADC_InitStructure.ADC_ScanConvMode = DISABLE; //多通道扫描模式 7 ?* _1 L- V/ G; v" m. J ADC_InitStructure.ADC_ContinuousConvMode = DISABLE; //是否启用连续转换模式 $ H M @1 I* p& i4 X; _/ T ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_T2_CC2; //触发方式 ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right; //数据右对齐 2 M0 E4 g' Q6 k' d ADC_InitStructure.ADC_NbrOfChannel = 1; //ADC规则转换通道数量 ADC_Init(ADC1, &ADC_InitStructure); 3 N$ G- L: \0 T: @1 i H9 i 3 @. f* d7 u. w' X6 M; P2 O /* ADC1 regular channel10 configuration */ //配置转换规则 ADC_RegularChannelConfig(ADC1, ADC_Channel_10, 1, ADC_SampleTime_1Cycles5); ; ]: w* k& ?, r- F / w o5 j ?( m2 R5 l! ` /* Enable ADC1 DMA */ + m" E$ M/ D' n8 }: [0 R ADC_DMACmd(ADC1, ENABLE); S+ [# l7 E, p 7 Z8 g8 }5 c8 E4 N( k/ v4 e /* Enable ADC1 */ ADC_Cmd(ADC1, ENABLE); . x" T+ a( T$ j t/ o /* Enable ADC1 reset calibaration register */ 0 v8 i9 j! J& v1 F' @1 u1 o ADC_ResetCalibration(ADC1); //复位ADC1 校准 /* Check the end of ADC1 reset calibration register */ while(ADC_GetResetCalibrationStatus(ADC1)); //等待复位完成 + T# g1 k& _$ `/ W/ M5 s /* Start ADC1 calibaration */ ADC_StartCalibration(ADC1); //开始ADC1 校准转换 3 o) i* U3 W9 C5 |5 M0 w /* Check the end of ADC1 calibration */ 5 U4 H& S( z5 D: F% ] while(ADC_GetCalibrationStatus(ADC1)); //等待转换完成 9 j' m" E; a# H3 Z3 R& S ADC_ExternalTrigConvCmd( ADC1, ENABLE); //使能或者失能ADCx的经外部触发启动转换功能 6 G- k1 d9 Z9 P* [ } 9 G) k2 c" b9 J1 ]. j DMA中断服务程序: ' `) h, ?" n2 Q! G; V1 Q /******************************************************************************* * Function Name : DMAChannel1_IRQHandler + s8 R4 C+ W1 A! C2 [2 G* I( T * Description : This function handles DMA Stream 1 interrupt request. 1 s) L0 x& \: b; j) B9 z& ] *******************************************************************************/ void DMAChannel1_IRQHandler(void) { DMA_Cmd(DMA_Channel1, DISABLE); //关闭DMA通道1 9 f6 B- R$ a/ c% c0 H$ z+ D; G for(count=64;count |
最全USB HID开发资料,悉心整理一个月,亲自测试
【源码】STLINK-V3MINI 高速USB仿真器,成功改刷【高速CMSIS-DAP】
STM32 USB CDC 虚拟多串口
实战经验 | 选择USBX模块生成USB CDC ACM无PD的项目
STM32 USB HID键盘例程
刘氓兔的杂谈【001】-片上USB 高速PHY
【经验分享】在进行 USB CDC 类开发时,无法发送 64整数倍的数据
在线直播|无需编写任何代码即可在STM32上实现USB-C Power Delivery
圈圈发布USB图书第二版有感,以及分享一些我学习USB过程...
USB Audio设计与实现
微信公众号
手机版
RE:我的毕设总结-用STM32的高速AD和USB2.0做简易示波器
RE:我的毕设总结-用STM32的高速AD和USB2.0做简易示波器
呵呵,双踪的呵呵......
RE:我的毕设总结-用STM32的高速AD和USB2.0做简易示波器
http://www.ourdev.cn/bbs/bbs_content.jsp?bbs_sn=2228838&bbs_page_no=1&bbs_id=3043
回复:我的毕设总结-用STM32的高速AD和USB2.0做简易示波器
RE:我的毕设总结-用STM32的高速AD和USB2.0做简易示波器
回复:我的毕设总结-用STM32的高速AD和USB2.0做简易示波器
RE:我的毕设总结-用STM32的高速AD和USB2.0做简易示波器
回复:我的毕设总结-用STM32的高速AD和USB2.0做简易示波器
RE:我的毕设总结-用STM32的高速AD和USB2.0做简易示波器
RE:我的毕设总结-用STM32的高速AD和USB2.0做简易示波器
不错
回复:我的毕设总结-用STM32的高速AD和USB2.0做简易示波器
RE:我的毕设总结-用STM32的高速AD和USB2.0做简易示波器
RE:我的毕设总结-用STM32的高速AD和USB2.0做简易示波器
回复:我的毕设总结-用STM32的高速AD和USB2.0做简易示波器
强烈支持阿
最近adc正看不懂呢