最全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做简易示波器
回复:我的毕设总结-用STM32的高速AD和USB2.0做简易示波器
万分谢谢!tianst20@163.com
回复:我的毕设总结-用STM32的高速AD和USB2.0做简易示波器
RE:我的毕设总结-用STM32的高速AD和USB2.0做简易示波器
RE:我的毕设总结-用STM32的高速AD和USB2.0做简易示波器
RE:我的毕设总结-用STM32的高速AD和USB2.0做简易示波器
回复:我的毕设总结-用STM32的高速AD和USB2.0做简易示波器
今天是3月9号,再过3个月就要大学毕业了,最近工作找得郁闷。干脆躲回实验室,总结下我的毕业设计,算是把自己的大学画个句号吧。
% J5 o5 I. q8 ?* @+ e2 j
1 概述
毕业设计的题目是“基于STM32的简易示波器设备驱动的研究”,就是利用STM32的AD采集波形数据,然后通过它内置的USB2.0把数据传到PC上面显示出来。框图如下。( @* g [/ b ?) U# F* W2 A- t
题目一共是两个人做,我和我搭档耗子。他做信号处理,采集部分。我做USB在PC下的驱动开发,以及上位机界面的显示。USB固件那部分我们是一起完成的。 8 T! s/ ]- H6 Q' t; `
这个课题从08年10月开始,到12月圣诞前夕基本完成。最后基本实现了波形的显示,实测最大可以测量的频率是200KHZ, J6 `7 M6 z2 F$ n
效果图如下: " G, F" R4 m! o8 I7 r5 a: {
<font color="#000000">方波的图形好像不是很漂亮会有上升沿老是有尖刺,还需要作软件做处理。没截出来,
8 C- S4 F$ O* C) k* u4 I
最后能出数据离不开很多前辈的经验。有些地方是借鉴了他们的东西。现在先列出来。
第一个是21IC上面alien2006原帖地址。他也是做了一个简易示波器,但是用的是以太网传输。采集部分我很多借鉴了他的方案。
第二个是电脑圈圈,他对USB的理解令我钦佩。我能搞出驱动,他提供的源码包非常重要,有些代码也是直接在他的基础上修改的。 / ]% H% |: T1 `6 y6 L; m7 s
2 整体方案 2 A5 g1 B% q7 U1 D0 z
先说下一次完整的采集,比如外面进来的波形是正弦波,波形电压有正负,STM32单片机的AD只能采集0~3.6V的电压,所以要对信号进行 处理。也就是需要一个模拟前端电路,把电压抬上去。接着就是采集了,STM32的AD可以用外设进行触发,这里用定时器进行触发。每过一个单位时间AD开 启一次,采集一个点,这样采集的频率只要调整这个单位时间也就是定时器就可以控制了。采集了一桢数据,比如200个点。DMA中断被触发,开启USB,把 数据发送到上位机,然后显示出来。 6 y) i; p" B. W
整个过程大体就是这样了,还有一个很重要的环节补充下。熟悉示波器的人都知道示波器有个触发概念。像刚才这样显示的话,比如前一帧数据是波 峰开始显示,后一帧是波谷开始。这样显示出来的波形就是乱的,于是为了解决这个问题,就需要做触发,也就是保证每次采集的起始电位相同。我们的采用的是用 外中断的形式,外面波形数据先不采集,先让它通过一个比较器,比如比较器的基准电压是1V,也就是每次都和1V比较,低于1V输出低电平,高于1V输出高 电平。当数据电压大于1V的时候,比较器输出高电平,高电平接到单片机外中断口,这样外中断就被触发。然后开始采集,这样就能保证每帧数据的起始点都相 同。
这个方案是大体方案,后来做了下修改。就是让AD一直在采集。外中断触发了后开启的是DMA,AD一直开着,只是控制DMA什么时候去取。从哪里开始取,取多少个点。下位机部分不再详说,贴下主要的程序,大侠们随便看看,多多指教。 9 }1 H6 d# Z6 c" l; q
定时器设置代码如下:
/*******************************************************************************
* Function Name : TIM_Configuration " K# o6 t5 o! a, _+ d G2 b. w
* Description : Configure the TIM2.
*******************************************************************************/
void TIM2_Configuration(void) $ m+ W5 I( g7 H4 w: h6 K& _
{ * W1 m6 d: d1 J5 x3 j8 T: p$ |
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure; ! p7 V2 q* D0 m& ~
TIM_OCInitTypeDef TIM_OCInitStructure;
( f( T7 V* A. l# k3 m. C: r
TIM_DeInit(TIM2); //TIM2初始化
/* Time base configuration */ 2 _; C8 ~ O5 @# `# G& y# z
TIM_TimeBaseStructure.TIM_Period = 18; //设置了下一个更新事件装入活动的自动重装载寄存器周期的值
TIM_TimeBaseStructure.TIM_Prescaler = 0; //设置了用来作为TIMx时钟频率除数的预分频值
TIM_TimeBaseStructure.TIM_ClockDivision = 0; //设置了时钟分割 5 b3 {- V' ]$ ?& h# O1 f$ p
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; //选择了计数器模式向上计数 G' x5 A6 a& t6 N
TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure); //根据TIM_TimeBaseInitStruct中指定的参数初始化TIMx 5 U* `# j; X" s% L9 `" ^6 [
/* Prescaler configuration */ * w9 k% d$ _9 t5 v+ F3 Y
// TIM_PrescalerConfig(TIM2, 1, TIM_PSCReloadMode_Update); //设置TIMx重载次数 预分频值在更新事件装入 9 E# C7 ^) S+ E
2 y* j* ~+ Q$ F5 X8 `
/* TIM_OCMode_Toggle Mode configuration: Channel2 */
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_Toggle; & O. m! m* [0 N) N5 _0 N* g# C
TIM_OCInitStructure.TIM_Channel = TIM_Channel_2; h6 I M8 i' F5 T% T0 @
TIM_OCInitStructure.TIM_Pulse = 9; 9 @( {* @3 a! j6 g
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_Low; //TIM输出比较极性低
TIM_OCInit(TIM2, &TIM_OCInitStructure); 5 R# I' d2 O7 x7 m- g. R
TIM_ARRPreloadConfig(TIM2, ENABLE); //使能或者失能TIMx在ARR上的自动装载寄存器 3 p, ~" `/ S- I& B: A
/* TIM2 enable counter */ $ E9 |* Q% n. A& s7 @
TIM_Cmd(TIM2, ENABLE); //使能TIMx外设 : x. s4 h, b' t) z! n
}
AD以及DMA设置代码: ~" G$ P- _4 B: u. Y0 `% W7 o
/******************************************************************************* * D# k4 w4 x1 v; P/ c, E
* Function Name : ADC_Configuration % n E5 x8 a1 ^) T/ R
* Description : Configure the ADC1. 7 i. V- q0 c( X( U% {) s
*******************************************************************************/
void ADC_Configuration(void)
{ % s f0 q6 ]6 w' c+ O3 ?
/* DMA channel1 configuration ----------------------------------------------*/
DMA_DeInit(DMA_Channel1); //复位DMA_Channel1
DMA_InitStructure.DMA_PeripheralBaseAddr = ADC1_DR_Address; //外围设备地址 ' d! U* I% F0 G
DMA_InitStructure.DMA_MemoryBaseAddr = (u32)&ADC_Data[0]; //memory 地址
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC; //外围设备做为源
DMA_InitStructure.DMA_BufferSize = 1024; //数据单元尺寸 2 x% M8 S. l' p+ ^5 o
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; //外围地址是否自动增长 disable不增长
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable; //memory 是否自动增长 - a# Q* w% _/ l5 J. T
DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord; //外围设备寄存器尺寸16位 2 y+ w! K* b) w- }* k9 _
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转换 2 |( i2 q8 v5 V" O
DMA_Init(DMA_Channel1, &DMA_InitStructure); //初始化DMA通道1 , w7 F; c% v5 q T: W5 ^$ V0 P
. D7 F# t9 a1 g. s; L, e( I, I
/* DMA IT enable */ . @- u* n1 b' T( T" k
DMA_ITConfig(DMA_Channel1, DMA_IT_TC, ENABLE); //使能DMA传输完成中断
/* ADC1 configuration ------------------------------------------------------*/
ADC_InitStructure.ADC_Mode = ADC_Mode_Independent; //ADC 工作模式 ADC1 ADC2 单独工作
ADC_InitStructure.ADC_ScanConvMode = DISABLE; //多通道扫描模式
ADC_InitStructure.ADC_ContinuousConvMode = DISABLE; //是否启用连续转换模式 2 ^9 X- O- \* b% h$ O% c( u
ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_T2_CC2; //触发方式 - R) o3 o) w$ N. z C
ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right; //数据右对齐
ADC_InitStructure.ADC_NbrOfChannel = 1; //ADC规则转换通道数量 : z7 K) d, m: F$ z% o
ADC_Init(ADC1, &ADC_InitStructure);
/* ADC1 regular channel10 configuration */ //配置转换规则 $ J5 P- x. g* R% U
ADC_RegularChannelConfig(ADC1, ADC_Channel_10, 1, ADC_SampleTime_1Cycles5); # L7 V$ @' I0 ]8 b6 j
# S: e! R B( A2 z
/* Enable ADC1 DMA */
ADC_DMACmd(ADC1, ENABLE); 0 H( u. V/ s: T) E/ F/ h
* S9 F7 {8 O _$ [: v# f
/* Enable ADC1 */ 2 l& q$ S2 w! s7 S9 h" ^$ |
ADC_Cmd(ADC1, ENABLE); % u9 P6 _' _9 {* Z8 S8 j4 t
9 E% m% X( H% r& I2 @& Z
/* Enable ADC1 reset calibaration register */
ADC_ResetCalibration(ADC1); //复位ADC1 校准
/* Check the end of ADC1 reset calibration register */ % m; y9 P8 V! C; A! e# e
while(ADC_GetResetCalibrationStatus(ADC1)); //等待复位完成 D. _% L3 f1 a* }7 p4 E- R2 n
5 H, B' ]" |! M7 Q3 c& _
/* Start ADC1 calibaration */ $ C3 ?3 L4 k) c7 Z5 r% T
ADC_StartCalibration(ADC1); //开始ADC1 校准转换
/* Check the end of ADC1 calibration */ , t2 f; u6 }0 ~% J9 S$ b
while(ADC_GetCalibrationStatus(ADC1)); //等待转换完成 - h3 @" S- n/ H# B/ M
ADC_ExternalTrigConvCmd( ADC1, ENABLE); //使能或者失能ADCx的经外部触发启动转换功能
} % l3 ^% ], z1 p6 K) y# s
DMA中断服务程序: 8 [% E% u l: V& {6 C2 M
/******************************************************************************* # O( j4 ]. a1 B& f. G3 n+ O0 n+ N9 J
* Function Name : DMAChannel1_IRQHandler
* Description : This function handles DMA Stream 1 interrupt request. ' p/ A" X! D3 I t& M- C1 g
*******************************************************************************/
void DMAChannel1_IRQHandler(void)
{ - C9 ]6 n- S( g/ p
DMA_Cmd(DMA_Channel1, DISABLE); //关闭DMA通道1 4 W" y: b1 _3 |' @" Z$ m' x
for(count=64;count
RE:我的毕设总结-用STM32的高速AD和USB2.0做简易示波器
回复:我的毕设总结-用STM32的高速AD和USB2.0做简易示波器
RE:我的毕设总结-用STM32的高速AD和USB2.0做简易示波器
回复:我的毕设总结-用STM32的高速AD和USB2.0做简易示波器
回复:我的毕设总结-用STM32的高速AD和USB2.0做简易示波器
RE:我的毕设总结-用STM32的高速AD和USB2.0做简易示波器
回复:我的毕设总结-用STM32的高速AD和USB2.0做简易示波器
RE:我的毕设总结-用STM32的高速AD和USB2.0做简易示波器