|
今天是3月9号,再过3个月就要大学毕业了,最近工作找得郁闷。干脆躲回实验室,总结下我的毕业设计,算是把自己的大学画个句号吧。 3 ~1 o) x( @8 E1 d! ] 1 概述 4 W) L0 s+ C: Q3 e$ J& a 毕业设计的题目是“基于STM32的简易示波器设备驱动的研究”,就是利用STM32的AD采集波形数据,然后通过它内置的USB2.0把数据传到PC上面显示出来。框图如下。 题目一共是两个人做,我和我搭档耗子。他做信号处理,采集部分。我做USB在PC下的驱动开发,以及上位机界面的显示。USB固件那部分我们是一起完成的。 - j. [7 p: N m' \5 k; b2 K9 Q( r 这个课题从08年10月开始,到12月圣诞前夕基本完成。最后基本实现了波形的显示,实测最大可以测量的频率是200KHZ, / V) P( T9 a+ r 效果图如下: " O) l. |, B$ A3 e% m" Q + u2 ^5 I' D! ` N. x4 a <font color="#000000">方波的图形好像不是很漂亮会有上升沿老是有尖刺,还需要作软件做处理。没截出来, C" l) u" Q0 F# h6 k- M 最后能出数据离不开很多前辈的经验。有些地方是借鉴了他们的东西。现在先列出来。 ; a" [4 j0 F. ?& R3 T" p 第一个是21IC上面alien2006原帖地址。他也是做了一个简易示波器,但是用的是以太网传输。采集部分我很多借鉴了他的方案。 $ ^3 ~/ J* ]3 i/ S/ m% o7 n9 N 第二个是电脑圈圈,他对USB的理解令我钦佩。我能搞出驱动,他提供的源码包非常重要,有些代码也是直接在他的基础上修改的。 , z' T3 e3 H; L ' ~! Q# S0 r" @ 2 整体方案 % Y) @2 o. l8 d$ j7 |- a; g* B 先说下一次完整的采集,比如外面进来的波形是正弦波,波形电压有正负,STM32单片机的AD只能采集0~3.6V的电压,所以要对信号进行 处理。也就是需要一个模拟前端电路,把电压抬上去。接着就是采集了,STM32的AD可以用外设进行触发,这里用定时器进行触发。每过一个单位时间AD开 启一次,采集一个点,这样采集的频率只要调整这个单位时间也就是定时器就可以控制了。采集了一桢数据,比如200个点。DMA中断被触发,开启USB,把 数据发送到上位机,然后显示出来。 整个过程大体就是这样了,还有一个很重要的环节补充下。熟悉示波器的人都知道示波器有个触发概念。像刚才这样显示的话,比如前一帧数据是波 峰开始显示,后一帧是波谷开始。这样显示出来的波形就是乱的,于是为了解决这个问题,就需要做触发,也就是保证每次采集的起始电位相同。我们的采用的是用 外中断的形式,外面波形数据先不采集,先让它通过一个比较器,比如比较器的基准电压是1V,也就是每次都和1V比较,低于1V输出低电平,高于1V输出高 电平。当数据电压大于1V的时候,比较器输出高电平,高电平接到单片机外中断口,这样外中断就被触发。然后开始采集,这样就能保证每帧数据的起始点都相 同。 这个方案是大体方案,后来做了下修改。就是让AD一直在采集。外中断触发了后开启的是DMA,AD一直开着,只是控制DMA什么时候去取。从哪里开始取,取多少个点。下位机部分不再详说,贴下主要的程序,大侠们随便看看,多多指教。 " q C7 l8 [ I j9 \ 定时器设置代码如下: 1 u/ J Z" a* a' U! t; f /******************************************************************************* * Function Name : TIM_Configuration * Description : Configure the TIM2. *******************************************************************************/ + E0 y& i: N5 a1 S void TIM2_Configuration(void) { 6 T# X# \" P/ ^8 y TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure; ! F/ t+ i" a& V2 u TIM_OCInitTypeDef TIM_OCInitStructure; $ x% l) ]4 X" |6 p9 T" J TIM_DeInit(TIM2); //TIM2初始化 # j7 f; Q B' E7 m0 g /* Time base configuration */ TIM_TimeBaseStructure.TIM_Period = 18; //设置了下一个更新事件装入活动的自动重装载寄存器周期的值 TIM_TimeBaseStructure.TIM_Prescaler = 0; //设置了用来作为TIMx时钟频率除数的预分频值 4 D( { b. D$ Y TIM_TimeBaseStructure.TIM_ClockDivision = 0; //设置了时钟分割 TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; //选择了计数器模式向上计数 TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure); //根据TIM_TimeBaseInitStruct中指定的参数初始化TIMx 2 [+ o4 q' c0 F- Y5 I - u2 L5 V4 z! ^! k0 b8 [. Q /* Prescaler configuration */ // TIM_PrescalerConfig(TIM2, 1, TIM_PSCReloadMode_Update); //设置TIMx重载次数 预分频值在更新事件装入 , F: F# Y. c4 N7 X& h: n& {8 V" h ' R- y+ B& h$ Y" c& @6 J /* TIM_OCMode_Toggle Mode configuration: Channel2 */ TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_Toggle; TIM_OCInitStructure.TIM_Channel = TIM_Channel_2; r4 c0 q2 a9 { ~3 N# _6 s+ I8 { TIM_OCInitStructure.TIM_Pulse = 9; 6 W5 k5 ?5 m, h) r" }" C/ m TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_Low; //TIM输出比较极性低 ( m7 o1 I! Y1 }& Y9 W X TIM_OCInit(TIM2, &TIM_OCInitStructure); 6 ?# u/ g2 X6 r3 R- M TIM_ARRPreloadConfig(TIM2, ENABLE); //使能或者失能TIMx在ARR上的自动装载寄存器 7 D' w3 ]! v8 Z- Y 6 l: d5 T |% a$ p- q6 S9 h6 B /* TIM2 enable counter */ TIM_Cmd(TIM2, ENABLE); //使能TIMx外设 2 G' Q! G ^- V; i( m } AD以及DMA设置代码: /******************************************************************************* * Function Name : ADC_Configuration * o$ ^7 n1 y7 K$ S- u, b4 e * Description : Configure the ADC1. % v# s- N, |5 f8 a+ l% Z9 [4 M *******************************************************************************/ $ a7 w2 X3 Z/ {/ N void ADC_Configuration(void) . H3 p0 A: `3 l$ I: C1 d# v { + C, L* k" p. s* l- P* n5 W /* DMA channel1 configuration ----------------------------------------------*/ $ c1 H2 g$ |7 h DMA_DeInit(DMA_Channel1); //复位DMA_Channel1 DMA_InitStructure.DMA_PeripheralBaseAddr = ADC1_DR_Address; //外围设备地址 1 f; d, j+ O; O% W DMA_InitStructure.DMA_MemoryBaseAddr = (u32)&ADC_Data[0]; //memory 地址 DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC; //外围设备做为源 1 k% S; u( w" ~$ Q' @ DMA_InitStructure.DMA_BufferSize = 1024; //数据单元尺寸 DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; //外围地址是否自动增长 disable不增长 DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable; //memory 是否自动增长 / N, n: L0 b1 f! ] DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord; //外围设备寄存器尺寸16位 DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord; //memory尺寸16位 DMA_InitStructure.DMA_Mode = DMA_Mode_Circular; //DMA 循环模式 2 U0 a% m* h4 ^0 {% X DMA_InitStructure.DMA_Priority = DMA_Priority_High; //DMA 通道优先级 DMA_InitStructure.DMA_M2M = DMA_M2M_Disable; //memory- to-memory转换 7 l7 j2 `* u& b. h( T DMA_Init(DMA_Channel1, &DMA_InitStructure); //初始化DMA通道1 ( L' Q9 u) \0 I" i/ o0 d' ` /* DMA IT enable */ 6 ?/ |8 c% C0 i2 u, t$ y0 N DMA_ITConfig(DMA_Channel1, DMA_IT_TC, ENABLE); //使能DMA传输完成中断 7 }. @% {% c" ^* u) a4 H2 f 7 H& w2 a: Z/ ~. R7 x: m /* ADC1 configuration ------------------------------------------------------*/ ADC_InitStructure.ADC_Mode = ADC_Mode_Independent; //ADC 工作模式 ADC1 ADC2 单独工作 3 @ v9 N* E, L- r/ t. } ADC_InitStructure.ADC_ScanConvMode = DISABLE; //多通道扫描模式 ADC_InitStructure.ADC_ContinuousConvMode = DISABLE; //是否启用连续转换模式 ; u( ^6 z. d% k0 ^4 \' j ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_T2_CC2; //触发方式 ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right; //数据右对齐 5 h; Z/ F, l9 H8 L) J5 s6 a ADC_InitStructure.ADC_NbrOfChannel = 1; //ADC规则转换通道数量 ADC_Init(ADC1, &ADC_InitStructure); /* ADC1 regular channel10 configuration */ //配置转换规则 8 |. i- t% u5 a4 z$ H: U/ ~, L ADC_RegularChannelConfig(ADC1, ADC_Channel_10, 1, ADC_SampleTime_1Cycles5); 2 T9 u8 a! G7 t5 J- t2 F ( x3 ^* {! F) s5 n) f /* Enable ADC1 DMA */ ADC_DMACmd(ADC1, ENABLE); /* Enable ADC1 */ ADC_Cmd(ADC1, ENABLE); - b, u: Q' @/ a% F! Y- J /* Enable ADC1 reset calibaration register */ ADC_ResetCalibration(ADC1); //复位ADC1 校准 /* Check the end of ADC1 reset calibration register */ ; R( J6 s6 c W( N while(ADC_GetResetCalibrationStatus(ADC1)); //等待复位完成 ( Y9 ?, H3 Z& X. Q) \3 j& y$ U) Y /* Start ADC1 calibaration */ ; T/ P u+ x9 N ADC_StartCalibration(ADC1); //开始ADC1 校准转换 /* Check the end of ADC1 calibration */ 0 z( s5 Z; ?; q# }$ L! r while(ADC_GetCalibrationStatus(ADC1)); //等待转换完成 / v& Q* m7 W/ K4 Z" S- E* ?# D ADC_ExternalTrigConvCmd( ADC1, ENABLE); //使能或者失能ADCx的经外部触发启动转换功能 ; U# V x4 O I7 ^% P& n: n8 p } DMA中断服务程序: . R0 Q. W4 \' S; o /******************************************************************************* ' Z# L# t& P# K8 E5 i c% r9 z * Function Name : DMAChannel1_IRQHandler * Description : This function handles DMA Stream 1 interrupt request. *******************************************************************************/ / i. u( K6 f/ D void DMAChannel1_IRQHandler(void) { 3 Z4 G/ J( t3 O; e5 E DMA_Cmd(DMA_Channel1, DISABLE); //关闭DMA通道1 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做简易示波器
强烈支持阿9 t4 I, i+ M& ~2 d8 ]
最近adc正看不懂呢