本帖最后由 电子星辰 于 2019-1-14 17:27 编辑
0 N ^4 j* ]( k8 i; L; q
2 D2 O6 V6 q" N- @) ?' M& n本文在NUCLEO-G071RB核心板平台上,使用CubeMX和HAL库,通过12位ADC,连续DMA采样和不连续转换,单通道(或双通道)采集,DAC连接外部引脚通过DMA输出的一个简易正弦波。最后通过NUCLEO自带的低功耗串口1打印采集到的一个电压。
0 ]% k' t9 n5 _6 w7 N5 W2 Y! {2 A& M6 ]9 X
一、Cube设置
* T* l. d/ V* T8 B' @& K4 ~, N' C首先我是通过选择Board新建的工程,所以会有一个NUCLEO外设初始化的选择,可以省点力气。通过MCU新建也是一样的。# ^" p5 g! S# \; K
( |& f! X3 {: E4 f
, z5 G5 V( ] M/ g# o其次,RCC、SYS、LPUART都是默认的。
" _; N, d/ }- M4 u, [$ A; L
7 S& \* g- }" T
用SWD下载的话记得勾选“Serial Wire”,要不下载之后就不能再下载了。不过这个板子有个复位跳线帽应该好整,出了问题可以复位下载试试。8 z- N, f! u+ N& c
3 A5 y6 S4 l4 K% x
LPUART1,异步模式自己改下常用参数就行了,“Data Direction”模式选择是我个人只需要发送。. Q) F/ d, e4 G, E# C' G& F- _
, @& c. {$ {7 y# \; n1 r: [! }- o+ W# N) Y' I
然后是DAC和定时器。8 t3 \- U! ]* `/ C* \
DAC输出简单点,选择仅连接外部引脚就行了,另一个连接外设的选项就是连比较器之类的。需要通过定时器做触发事件(基本定时器就够了)。( M3 O" O) P4 s
$ U# `/ w: W3 E; V) ~2 a% G
DMA通道不用管,方向是内存到外设。选循环模式,到时候打开了就不用管。“Data Width”选1个Byte就够了,方便点,不用取高低位。
5 I c# D; s; u( M* D5 |. R
( s3 ]+ |; g M8 y" K8 t7 S* T定时器6是基本定时器,我只需要它一个计数触发的功能。/ [4 F9 d0 F$ ]/ f6 i
Prescaler是定时器预分频,定时器实际时钟频率为:主频/(Prescaler+1),7 d) F& X' ]4 v+ F
Counter Period是定时器周期,当定时器开始计数到Counter Period值并且重复计数寄存器(Repetition Counter)为0时更新定时器并生成对应事件和中断,
5 a. Z+ }9 q2 f! Z# L3 p D, R$ u3 q最终定时器频率计算为: 主频/(Prescaler+1)/(Repetition Counter+1)/(Counter Period+1(也可能不需+1)),比如需要产生20ms周期定时,可以设置为:64MHz/(63+1)/(0+1)/(19999+1)=50Hz,即20ms周期。我现在设置16位定时器的最大数,64分频的话就是65.535ms。
# a" E1 d$ i( B$ u1 m5 `6 D" B
# F' i0 H* {. i# M. v; n/ G
! `9 k# ?; v" f3 K" f
接着是ADC,我写了2个通道(但是只用了其中1个)。我没有选连续转换和DMA连续请求,所以在程序里还需要循环开启ADC转换。ScanConvMode扫描转换,是根据你的转换通道数自动设置的,转换序列中如果有超过一个通道需要转换的话,那么必须开启扫描模式,否则的话,时钟只转换第一个通道。1 b" f8 f, [ {( ]- C
# Q! j8 a, v, b- j1 LDMA通道不用管,方向从外设到内存。选的半字“Half Word”,代码里还需对取的值取低12有效位(因为上面ADC参数我是右对齐)。5 [) \% q: k7 X; c9 n* d. ]
- v/ J- h! r4 w
& p7 f# }: L: u5 t* i% C3 @
W! U$ [/ y) c! d9 ?9 K& L: `) g
HCLK改为64后直接回车,它会自动配置。. c6 \! j, X2 M6 _
( v4 s- ~3 m: V. Z) N; m3 l7 r! g% U }& f+ D# A p
最后是结果,一共6个值,0.04、0.66、1.33、1.98、2.63、3.27。符合我的预期。我的输出值是0x0,0x33,0x66,0x99,0xCC,0xFF。3 R L- |" h9 y& R7 g; Z
" e, {5 X0 ^8 M: j v8 t
- Q8 _3 F* C/ L* x3 Y7 k: }
" x2 k* d, p H) u% M D. Z
) a8 r; B/ ?6 V, f8 @- /* USER CODE BEGIN PD */3 @! A! ^4 N3 i" o& x$ q
- #define ADC_SAMPLE 2//采样数$ E$ e, C$ m. z. W8 l
- /* USER CODE END PD */+ D" E' U/ p; K0 g5 u0 ]1 Y
- . J" ?$ M' s* X% c( y) Y, M
- /* USER CODE BEGIN PV */
5 p! s- [* e: w$ P - __IO uint16_t ADC_ConvertedData[ADC_SAMPLE]={0};//DMA取得的ADC值( v! Q9 u) }) y! U
- const uint8_t aEscalator8bit[6] = {0x0,0x33,0x66,0x99,0xCC,0xFF};//DAC输出量表
. a- E& O. A' \2 B4 H - /* USER CODE END PV */
3 B* a% p* v5 i6 z H0 m: ]
4 r" D' O- {7 O- I4 n) N4 p7 v4 J- /* Private function prototypes -----------------------------------------------*/
/ U+ h, {! a( j - void SystemClock_Config(void);
$ a% F! `4 E2 \. k* I0 c - /* USER CODE BEGIN PFP */6 }( K [ C9 `# g9 X1 S% q( l
- int main(void)
/ S1 ]2 V) Z0 k - {9 b: ^% n% H/ Q
- /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
. f. k: N3 B' {/ O - HAL_Init();
" b( E$ X# V- G - /* Configure the system clock */
0 g9 B0 N& _; M: u( L - SystemClock_Config();' I7 [2 n* K2 s
- /* Initialize all configured peripherals */
0 d) V; y t( b, I" e2 J - MX_GPIO_Init();% G# `2 H. `7 [1 V7 d; n
- MX_DMA_Init();
1 C) A& @; P9 h9 T" E: N/ \- \- M - MX_DAC1_Init();4 r- {" }" ^" Z, T* b: X. V+ v0 v
- MX_TIM6_Init();
! [' ^* L, y6 L/ P - MX_ADC1_Init();
% q2 Y+ X9 I" X - MX_LPUART1_UART_Init();
. p& y4 z2 S& ] - /* USER CODE BEGIN 2 */3 }8 T; r& x. H; ^( D2 v. V/ o
- HAL_ADCEx_Calibration_Start(&hadc1);//ADC自校验
% D% Y* T, w" e F - HAL_ADC_Start_DMA(&hadc1,(uint32_t *)ADC_ConvertedData,ADC_SAMPLE);//启动AD转换并使能DMA传输和中断。ADC_ConvertedData的两个元素都是通道0的值
" y O; ` q5 K" Z3 Y3 ? - HAL_TIM_Base_Start(&htim6);//开启定时器6进行触发9 ^9 W9 M) j! _3 j' w8 w$ m5 w
- HAL_DAC_Start_DMA(&hdac1,DAC_CHANNEL_1,(uint32_t *)aEscalator8bit,6,DAC_ALIGN_8B_R);//启动DAC-DMA输出,循环输出6个8bit数 * {) B1 b) E) u! o
- /* USER CODE END 2 */; K. n6 | i2 [0 e' j3 F. a# Q
- " u j+ I U* h% j' A0 B
- /* Infinite loop */
( e- P8 k1 W3 I - /* USER CODE BEGIN WHILE */' u8 b0 _5 Q& W1 C9 L+ R
- while (1)8 e& J3 S( t$ m: Z% I5 v! w
- {
. t$ z' Y; C( D- u( d# n9 i - printf("%.2f ",((ADC_ConvertedData[0] & 0xFFF)*(3.3/4096)));//串口不是重点,这个用法可能有隐患。取有效位和换算一起进行/ ?( T, X5 H$ a) I4 y1 _) x
- HAL_ADC_Start(&hadc1);//因为是不连续转换,需重复开启
( H: H2 X8 {1 a% h/ i" I - /* USER CODE END WHILE */
~8 l$ y1 ?5 B; \3 M) o
; ~* ?- S- c- l- /* USER CODE BEGIN 3 */: {. t& i" P; f" \
- }6 V" Y! P$ G3 V. s7 f
- /* USER CODE END 3 */$ r \1 I9 S- `* O( @$ n1 z
- }
复制代码 - \! d1 Q9 ]2 q8 [ C! o# o
. q9 C: \: l$ L
好了,文末放下资料。
! O' Q0 D% U* i! Z! F源代码和Cube工程:
NUCLEO-G071_DACandADC.rar
(7.98 MB, 下载次数: 1523)
|
我家里的万用表很老了,精度我这测不了。不好说不好说
DAC输出来的是近似0V的值,比如说零点零几V。缓冲的话我是开的内部缓冲。