本帖最后由 电子星辰 于 2019-1-14 17:27 编辑 5 O. j0 H( J) \- h% \
7 {+ r( v" P3 M6 }3 ^本文在NUCLEO-G071RB核心板平台上,使用CubeMX和HAL库,通过12位ADC,连续DMA采样和不连续转换,单通道(或双通道)采集,DAC连接外部引脚通过DMA输出的一个简易正弦波。最后通过NUCLEO自带的低功耗串口1打印采集到的一个电压。
* m# y: R' N7 |& g+ K% E4 _1 E: k+ x5 H6 z
一、Cube设置
+ s" C; @. e! J( v8 \首先我是通过选择Board新建的工程,所以会有一个NUCLEO外设初始化的选择,可以省点力气。通过MCU新建也是一样的。$ k3 |# Z4 ^9 A( \% F
6 ?) k% q9 h# H# a4 J! c
# i8 q( K7 u; a( z* o7 I
其次,RCC、SYS、LPUART都是默认的。
8 ~9 S1 E8 t# ~- V' L/ a0 X9 l
* q _2 \9 m4 Z3 c" W用SWD下载的话记得勾选“Serial Wire”,要不下载之后就不能再下载了。不过这个板子有个复位跳线帽应该好整,出了问题可以复位下载试试。
* ]2 o+ I1 U6 q! V0 t
! y' ~/ `: m5 \! K" I) s
LPUART1,异步模式自己改下常用参数就行了,“Data Direction”模式选择是我个人只需要发送。/ |! W( J2 }. w7 O
9 i# f2 y+ ?& D m4 M
6 H0 F4 R5 g$ u. i6 b然后是DAC和定时器。/ R- `9 h ~% t3 T
DAC输出简单点,选择仅连接外部引脚就行了,另一个连接外设的选项就是连比较器之类的。需要通过定时器做触发事件(基本定时器就够了)。
6 p% s/ G$ p5 y& O8 ]. |
0 x2 _. M3 K- w l
DMA通道不用管,方向是内存到外设。选循环模式,到时候打开了就不用管。“Data Width”选1个Byte就够了,方便点,不用取高低位。$ ^# y7 W" W" c" @
% } N- E6 k7 `6 ]' p
定时器6是基本定时器,我只需要它一个计数触发的功能。! C! t; i g% Y) `9 T
Prescaler是定时器预分频,定时器实际时钟频率为:主频/(Prescaler+1),& ~& c5 y6 i$ q" F+ ]4 U
Counter Period是定时器周期,当定时器开始计数到Counter Period值并且重复计数寄存器(Repetition Counter)为0时更新定时器并生成对应事件和中断,. f, u |6 T. b8 a3 C9 ~( Y
最终定时器频率计算为: 主频/(Prescaler+1)/(Repetition Counter+1)/(Counter Period+1(也可能不需+1)),比如需要产生20ms周期定时,可以设置为:64MHz/(63+1)/(0+1)/(19999+1)=50Hz,即20ms周期。我现在设置16位定时器的最大数,64分频的话就是65.535ms。* e5 z7 V2 P8 J" I
/ }+ z* n4 \5 y- c4 W
7 c1 M. }0 \9 B接着是ADC,我写了2个通道(但是只用了其中1个)。我没有选连续转换和DMA连续请求,所以在程序里还需要循环开启ADC转换。ScanConvMode扫描转换,是根据你的转换通道数自动设置的,转换序列中如果有超过一个通道需要转换的话,那么必须开启扫描模式,否则的话,时钟只转换第一个通道。, L% W- J, T9 g0 x( |& O' T
7 ]# ~' W5 r+ jDMA通道不用管,方向从外设到内存。选的半字“Half Word”,代码里还需对取的值取低12有效位(因为上面ADC参数我是右对齐)。& d. T- a& B/ _+ I, g2 y
7 h' M) b, {7 T3 P5 X) a+ Q
o9 h- u+ K1 `! z" @8 G: |# t" h. j0 M6 ^
HCLK改为64后直接回车,它会自动配置。* M8 i$ w: r- j& @1 T4 ^# G/ l
. E: \# e8 }4 f' H8 Y2 S; v
+ N7 k1 \) @3 Q4 X& x2 D
最后是结果,一共6个值,0.04、0.66、1.33、1.98、2.63、3.27。符合我的预期。我的输出值是0x0,0x33,0x66,0x99,0xCC,0xFF。+ J' Z, i' s% u- S' j
0 R: w, X9 X. [; ~# R0 Y+ n9 Y6 @4 Y1 m
; A% b' Y) \# ?5 @
/ m6 W% q6 x. V& Q" h1 m- /* USER CODE BEGIN PD */
0 F) o. f( a, \+ N! a% Y# ? - #define ADC_SAMPLE 2//采样数) U5 Y( r2 D. t2 x
- /* USER CODE END PD */& p( ]; l) z% W2 ?
- $ h' H; O/ I# R
- /* USER CODE BEGIN PV */
F$ A4 {8 r+ z# G# l$ G' B8 \ I - __IO uint16_t ADC_ConvertedData[ADC_SAMPLE]={0};//DMA取得的ADC值
+ r- e: f( S( t - const uint8_t aEscalator8bit[6] = {0x0,0x33,0x66,0x99,0xCC,0xFF};//DAC输出量表
1 l# w. @7 j5 O' D - /* USER CODE END PV */
3 G: [( U; N: A- z; S9 o. j - + ~9 q3 u$ X8 ?3 I7 r9 M( [
- /* Private function prototypes -----------------------------------------------*/- }8 g" n0 k/ J3 j8 v
- void SystemClock_Config(void);
6 R1 j1 D; O" p5 y - /* USER CODE BEGIN PFP */
. F, w& y: ?+ ], I3 h - int main(void)
N+ n" S; s1 ~; o+ s7 Y/ Y. K - {
) ^8 f( b, ` f5 |& i - /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
& f/ G l; s' @6 X& e, h; q - HAL_Init();
$ O9 \" x4 K4 M0 @& X - /* Configure the system clock */
1 [9 H1 c2 X) Z, f$ O, V! h - SystemClock_Config();0 {: i! }! u+ y2 [
- /* Initialize all configured peripherals */' A0 M- ^0 z# I$ E7 r
- MX_GPIO_Init();; T9 ?$ W# r5 A& h
- MX_DMA_Init();
9 ?: [. j8 P L8 p) ` - MX_DAC1_Init();
; h; w! @9 h: f2 b1 s+ W3 X% T - MX_TIM6_Init();" D7 Y7 M* n0 D5 j; Z
- MX_ADC1_Init();
4 G1 ]. |) f" V) _6 v* ~ - MX_LPUART1_UART_Init();
- G+ n- M- c! r8 A) c: e, a! {+ K - /* USER CODE BEGIN 2 */
1 o y7 a( G, `# } - HAL_ADCEx_Calibration_Start(&hadc1);//ADC自校验7 `4 S- P" O: b, b* [/ K
- HAL_ADC_Start_DMA(&hadc1,(uint32_t *)ADC_ConvertedData,ADC_SAMPLE);//启动AD转换并使能DMA传输和中断。ADC_ConvertedData的两个元素都是通道0的值+ U* W Y/ m# t: K3 Z
- HAL_TIM_Base_Start(&htim6);//开启定时器6进行触发9 t" |7 ~5 l& Q$ r* e% B6 J" ^
- HAL_DAC_Start_DMA(&hdac1,DAC_CHANNEL_1,(uint32_t *)aEscalator8bit,6,DAC_ALIGN_8B_R);//启动DAC-DMA输出,循环输出6个8bit数 ! Z( c/ }/ r0 p
- /* USER CODE END 2 */( u4 V* Z4 ?0 h( V; ?) M) H: e: j
1 D" r! a# s5 J1 w* x- /* Infinite loop */# g( W: {& t$ x
- /* USER CODE BEGIN WHILE */
) @+ S: ^0 O" }* r - while (1)' @" ?" I; A$ G; l) h1 z* \; |
- { 1 Q% ? G" A& n4 Z: U
- printf("%.2f ",((ADC_ConvertedData[0] & 0xFFF)*(3.3/4096)));//串口不是重点,这个用法可能有隐患。取有效位和换算一起进行! Q4 M/ c, X: P8 I5 ^3 W2 a
- HAL_ADC_Start(&hadc1);//因为是不连续转换,需重复开启
( g0 E% n) L& z3 ?9 W% ]- @, i - /* USER CODE END WHILE */
+ |! C; n t& O, q& a& \& T4 Z- w - 4 O/ f$ A) P- {6 U2 }+ u$ |
- /* USER CODE BEGIN 3 */
- D& C* R O% e0 | - }" c! a2 ]+ Q1 f0 k% U& ]: i8 V
- /* USER CODE END 3 */
" l$ r5 _1 v1 t& @$ I - }
复制代码 - W) t4 \* ~3 ~0 m1 e
8 g; [' k/ o+ T( L: n7 U
好了,文末放下资料。
q3 W" q: F6 y7 B$ n# |源代码和Cube工程:
NUCLEO-G071_DACandADC.rar
(7.98 MB, 下载次数: 1526)
|
我家里的万用表很老了,精度我这测不了。不好说不好说
DAC输出来的是近似0V的值,比如说零点零几V。缓冲的话我是开的内部缓冲。