本帖最后由 电子星辰 于 2019-1-14 17:27 编辑
% i7 V' \6 S( ~+ u4 [! z' D7 c( Z6 ^# u1 g2 ~: D: k$ Q
本文在NUCLEO-G071RB核心板平台上,使用CubeMX和HAL库,通过12位ADC,连续DMA采样和不连续转换,单通道(或双通道)采集,DAC连接外部引脚通过DMA输出的一个简易正弦波。最后通过NUCLEO自带的低功耗串口1打印采集到的一个电压。: L. E4 i7 z- `
6 v; n5 U( F1 v0 T, f一、Cube设置$ W( I1 w1 o9 K: n
首先我是通过选择Board新建的工程,所以会有一个NUCLEO外设初始化的选择,可以省点力气。通过MCU新建也是一样的。
% u5 J6 N+ s) h6 D3 I2 P2 b
7 F/ j1 @% r" R& j' ^2 {* L6 {
/ ?% X2 L5 b2 B+ v4 J; ]其次,RCC、SYS、LPUART都是默认的。: @0 T. L* `) j0 Z
5 a! {$ u/ T( c2 Z4 }
用SWD下载的话记得勾选“Serial Wire”,要不下载之后就不能再下载了。不过这个板子有个复位跳线帽应该好整,出了问题可以复位下载试试。8 E6 j0 b/ K2 _* p
& `9 G5 F+ l8 V6 {' G6 d
LPUART1,异步模式自己改下常用参数就行了,“Data Direction”模式选择是我个人只需要发送。
" L% E6 h- p {
/ T$ P0 @& [, ~" t) [5 ]7 E: u5 w
然后是DAC和定时器。" z& D) X6 R& X4 c' A# ]2 W
DAC输出简单点,选择仅连接外部引脚就行了,另一个连接外设的选项就是连比较器之类的。需要通过定时器做触发事件(基本定时器就够了)。6 c: y' u4 B* @! L% ^. {
. y- {4 m) K$ l& B, ODMA通道不用管,方向是内存到外设。选循环模式,到时候打开了就不用管。“Data Width”选1个Byte就够了,方便点,不用取高低位。
# m, f# i; `( N- ^, g
. G- B/ } ~# A5 f1 ^& n
定时器6是基本定时器,我只需要它一个计数触发的功能。 ] Z7 Q' f6 u+ |% G
Prescaler是定时器预分频,定时器实际时钟频率为:主频/(Prescaler+1),
6 U8 I; i* Z L) D& p: l pCounter Period是定时器周期,当定时器开始计数到Counter Period值并且重复计数寄存器(Repetition Counter)为0时更新定时器并生成对应事件和中断,. }; y W$ L# A1 F8 H: a' p
最终定时器频率计算为: 主频/(Prescaler+1)/(Repetition Counter+1)/(Counter Period+1(也可能不需+1)),比如需要产生20ms周期定时,可以设置为:64MHz/(63+1)/(0+1)/(19999+1)=50Hz,即20ms周期。我现在设置16位定时器的最大数,64分频的话就是65.535ms。: |" d6 a7 z" u0 F( h( m
2 g9 U+ s9 G( U# B% b5 q9 R
( N3 ^; }* {& a$ T. T2 Z2 g接着是ADC,我写了2个通道(但是只用了其中1个)。我没有选连续转换和DMA连续请求,所以在程序里还需要循环开启ADC转换。ScanConvMode扫描转换,是根据你的转换通道数自动设置的,转换序列中如果有超过一个通道需要转换的话,那么必须开启扫描模式,否则的话,时钟只转换第一个通道。
' ? r1 e# c* a+ R( M9 G, h3 q
7 U( M6 ^4 n3 K" v) F) i( j1 VDMA通道不用管,方向从外设到内存。选的半字“Half Word”,代码里还需对取的值取低12有效位(因为上面ADC参数我是右对齐)。
. d7 P9 |/ }. V9 A# V
& _1 d3 _3 z8 ]' |; A
. G2 s7 q: T) [
5 a: p' S6 M4 e5 ?6 }. Q6 ~5 |HCLK改为64后直接回车,它会自动配置。
( F# W) X2 v' d& }; m- l
; j5 N0 a8 o; r3 P/ W: x2 t D
, F7 b |+ B4 ]( X( W+ Q5 t; O4 a最后是结果,一共6个值,0.04、0.66、1.33、1.98、2.63、3.27。符合我的预期。我的输出值是0x0,0x33,0x66,0x99,0xCC,0xFF。
( @0 S1 c- N2 A9 G+ ? ` r h4 H
. ?* A) F! \% ?# ?4 m. a# F# }+ G F
% v4 ?. H9 W# h* J0 k, ]1 {* U
0 Y* H! G7 i1 S+ S m- /* USER CODE BEGIN PD */' p) f7 @4 n# d, D" C1 e
- #define ADC_SAMPLE 2//采样数
# c/ \# G: L1 A9 \9 T% V) z, K - /* USER CODE END PD */
D0 u, A4 A2 v* h/ z - ; m# y# W8 i$ n9 a! a5 t! y% s
- /* USER CODE BEGIN PV */
5 w5 d# ~0 [$ Y- o7 ^9 B, L7 X - __IO uint16_t ADC_ConvertedData[ADC_SAMPLE]={0};//DMA取得的ADC值
& {" H1 T" B V2 {" c - const uint8_t aEscalator8bit[6] = {0x0,0x33,0x66,0x99,0xCC,0xFF};//DAC输出量表
* d" v0 S H+ z' H+ Z) H# U6 F - /* USER CODE END PV */
9 u) a2 U5 D" q2 F b, d
+ E0 h }. c" N( f% _) m- /* Private function prototypes -----------------------------------------------*/
' ]- T5 {# W" v+ ?8 G - void SystemClock_Config(void); I: {# T; u+ _8 W( r) ] W
- /* USER CODE BEGIN PFP */" D- k8 w: s; W2 V3 u, q4 r8 a
- int main(void)) H( x+ ^' H1 W8 Q# Y3 I |
- {
" u6 `: c! y$ C9 v( ? - /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
" j- i1 B) _/ D0 h, W - HAL_Init();
6 U. T+ r2 W2 _' ]5 H: m - /* Configure the system clock */6 T5 L2 J' [9 y- W
- SystemClock_Config();
* D8 Y( s* F2 U7 ^' O - /* Initialize all configured peripherals */
& u& h2 S5 v) w) `" v! P8 Z - MX_GPIO_Init();
# W; ^: y& V$ |1 P - MX_DMA_Init();
% V! u' ]& S3 {" f& O2 F5 I - MX_DAC1_Init();1 Z; y. y( k! p# `
- MX_TIM6_Init();1 K6 k% E* Q/ R/ F# ^& ~
- MX_ADC1_Init();: `! g. p- r5 Z* p
- MX_LPUART1_UART_Init();% f6 w2 _7 f% D: L
- /* USER CODE BEGIN 2 */+ }5 ?4 n' M. @7 K5 a6 n$ T+ O
- HAL_ADCEx_Calibration_Start(&hadc1);//ADC自校验" G# v3 ~6 A1 l i( H4 y5 C
- HAL_ADC_Start_DMA(&hadc1,(uint32_t *)ADC_ConvertedData,ADC_SAMPLE);//启动AD转换并使能DMA传输和中断。ADC_ConvertedData的两个元素都是通道0的值1 x: A1 t0 H6 K4 Q* |% \
- HAL_TIM_Base_Start(&htim6);//开启定时器6进行触发$ O, D9 P2 C+ n' i; e
- HAL_DAC_Start_DMA(&hdac1,DAC_CHANNEL_1,(uint32_t *)aEscalator8bit,6,DAC_ALIGN_8B_R);//启动DAC-DMA输出,循环输出6个8bit数 8 q1 z- G1 ^% k" S7 \, c0 \1 m
- /* USER CODE END 2 */
5 L" Z$ l; D- U: N- G$ ^0 h7 i
9 e2 {6 ~+ a ^- /* Infinite loop */
4 C* _/ F. ]9 g7 ?, ] - /* USER CODE BEGIN WHILE */( Y6 m" v, Q# \* J+ I. R4 M& T
- while (1)& |5 ]0 l E i9 `
- { * r. E1 a$ K' T4 @9 d0 p
- printf("%.2f ",((ADC_ConvertedData[0] & 0xFFF)*(3.3/4096)));//串口不是重点,这个用法可能有隐患。取有效位和换算一起进行
; S; p x j5 @; h' S+ D - HAL_ADC_Start(&hadc1);//因为是不连续转换,需重复开启, k" E4 ^7 p* U' V4 i2 L
- /* USER CODE END WHILE */
( }% Z3 [6 `& @! M( B - & S7 F9 u- M' J8 j8 s1 T4 j) s+ Z
- /* USER CODE BEGIN 3 */
1 E! c9 g, N7 U5 r D - }( b, W, g0 \- `' R9 b
- /* USER CODE END 3 */% l( `7 l3 d1 d1 P
- }
复制代码
7 o8 W0 x& \3 _9 l" k" v
8 @) A2 v# A6 ?" |% V. ?! t好了,文末放下资料。( m. s" V7 F4 R0 M4 Q1 c7 e
源代码和Cube工程:
NUCLEO-G071_DACandADC.rar
(7.98 MB, 下载次数: 1523)
|
我家里的万用表很老了,精度我这测不了。不好说不好说
DAC输出来的是近似0V的值,比如说零点零几V。缓冲的话我是开的内部缓冲。