本帖最后由 电子星辰 于 2019-1-14 17:27 编辑
$ t9 L% {: {+ ?& o) v" l" \8 F
/ E/ Y( S" T+ l3 g本文在NUCLEO-G071RB核心板平台上,使用CubeMX和HAL库,通过12位ADC,连续DMA采样和不连续转换,单通道(或双通道)采集,DAC连接外部引脚通过DMA输出的一个简易正弦波。最后通过NUCLEO自带的低功耗串口1打印采集到的一个电压。
+ B3 Y0 y% x; c. x& [8 \. M' X) }7 [6 _: k) k
一、Cube设置% D; H: ` B% _
首先我是通过选择Board新建的工程,所以会有一个NUCLEO外设初始化的选择,可以省点力气。通过MCU新建也是一样的。& g8 @. c0 \5 d* {
& D! ^: T6 ~; h/ _9 O; a1 S7 w5 u* e. ^2 i2 f7 t# V" F
其次,RCC、SYS、LPUART都是默认的。
* l y, E+ D2 c& h6 c1 C
! K) t9 m% t U$ D5 m7 s3 L, W
用SWD下载的话记得勾选“Serial Wire”,要不下载之后就不能再下载了。不过这个板子有个复位跳线帽应该好整,出了问题可以复位下载试试。, h+ O; Z6 q1 k$ G& S7 E- _
5 D3 T6 i0 e) m, I' B- ?3 U
LPUART1,异步模式自己改下常用参数就行了,“Data Direction”模式选择是我个人只需要发送。
1 E7 ?* \3 W* t6 H% O( q2 `; ?! J
& Y3 H, ~- A/ E z& ^ l& n, b
& ~" M1 Z9 K# @ ~: f然后是DAC和定时器。$ ~0 w/ a7 X/ p
DAC输出简单点,选择仅连接外部引脚就行了,另一个连接外设的选项就是连比较器之类的。需要通过定时器做触发事件(基本定时器就够了)。! W d+ U# O9 e2 |
8 ]1 \) {1 t4 ]2 DDMA通道不用管,方向是内存到外设。选循环模式,到时候打开了就不用管。“Data Width”选1个Byte就够了,方便点,不用取高低位。
[; L' q# y% O2 }
; p2 D: U7 y& j( A( w; G
定时器6是基本定时器,我只需要它一个计数触发的功能。; }0 _+ ]/ |- x0 J
Prescaler是定时器预分频,定时器实际时钟频率为:主频/(Prescaler+1),
3 e9 C. z, B0 x9 B! vCounter Period是定时器周期,当定时器开始计数到Counter Period值并且重复计数寄存器(Repetition Counter)为0时更新定时器并生成对应事件和中断,! y7 ]! v v; g+ S$ j1 v- J
最终定时器频率计算为: 主频/(Prescaler+1)/(Repetition Counter+1)/(Counter Period+1(也可能不需+1)),比如需要产生20ms周期定时,可以设置为:64MHz/(63+1)/(0+1)/(19999+1)=50Hz,即20ms周期。我现在设置16位定时器的最大数,64分频的话就是65.535ms。- H5 U" `# X; {
0 L# i; ~8 d4 D
7 J/ ]* m7 j* K; J$ O6 J* k: Q) e接着是ADC,我写了2个通道(但是只用了其中1个)。我没有选连续转换和DMA连续请求,所以在程序里还需要循环开启ADC转换。ScanConvMode扫描转换,是根据你的转换通道数自动设置的,转换序列中如果有超过一个通道需要转换的话,那么必须开启扫描模式,否则的话,时钟只转换第一个通道。; n" W2 k6 T0 e) j! f/ N0 m
- u9 [& J$ {5 q2 M9 m" `; @: BDMA通道不用管,方向从外设到内存。选的半字“Half Word”,代码里还需对取的值取低12有效位(因为上面ADC参数我是右对齐)。' r; R# n7 b+ }& `1 n
V7 i) H- g' {3 w9 o2 N
- W; p, `. q9 W8 R. A" a' u
3 \# H+ v2 X! \- p' i$ b [) |" M
HCLK改为64后直接回车,它会自动配置。
4 {8 }1 O( g& {% L! \, l3 x$ |
) J, ^8 a% L* l
; ~) }9 m' H9 C& u7 I8 f1 a; z最后是结果,一共6个值,0.04、0.66、1.33、1.98、2.63、3.27。符合我的预期。我的输出值是0x0,0x33,0x66,0x99,0xCC,0xFF。# K- S- j: G, |. e/ @( a- b
& ?1 P: t( e) q3 S/ O4 ~
( S' `9 t- t, p: x) C3 C3 L7 s. f( e* A3 M/ x' ?2 Z
2 G) e/ g+ @2 K$ B/ J! J+ M- /* USER CODE BEGIN PD */
- y5 O2 d- r2 a$ t6 c) Z - #define ADC_SAMPLE 2//采样数6 N& r& u4 ^/ W0 b, G1 U
- /* USER CODE END PD */; I! {9 d1 |5 i' w" S
- # o) }6 y* n$ l% g, A5 h
- /* USER CODE BEGIN PV */
, D! h, [3 A- q7 k" {* G - __IO uint16_t ADC_ConvertedData[ADC_SAMPLE]={0};//DMA取得的ADC值
" i5 g3 ]0 T; x. W# M1 K - const uint8_t aEscalator8bit[6] = {0x0,0x33,0x66,0x99,0xCC,0xFF};//DAC输出量表% ^) x, o( s, }) S$ m
- /* USER CODE END PV */, G+ S8 {. x1 B# U/ c
3 |- m) T( V: w3 H- /* Private function prototypes -----------------------------------------------*/( k% F1 D) m+ _3 Y: A; _
- void SystemClock_Config(void);
1 O9 F) r$ E$ ? }" D# X$ o - /* USER CODE BEGIN PFP */
1 V& N% v) Z( g - int main(void)
$ f; g( P) n( K5 r1 { - {
* R& H8 @- P. N; s - /* Reset of all peripherals, Initializes the Flash interface and the Systick. */% x2 B7 X' u r2 x! o
- HAL_Init();
) z+ S( Y0 \" D& S% K - /* Configure the system clock */) r0 K, p9 o* h0 q& M
- SystemClock_Config();, F7 j8 v' a) ~
- /* Initialize all configured peripherals */
$ U3 I' M7 j1 o7 |# Q, b - MX_GPIO_Init();
5 O, E! H" M- u3 u - MX_DMA_Init();' J( n) ~' K: U$ {
- MX_DAC1_Init();
: K; ]( f, B# a0 {$ i0 X - MX_TIM6_Init();
) I$ r$ u/ G: T' r" f) Y1 K; z6 I, \2 G - MX_ADC1_Init();
+ H" R0 S: l8 m2 z( a6 I8 @; ] - MX_LPUART1_UART_Init();4 k. q) W6 q2 h1 f: j$ n# V
- /* USER CODE BEGIN 2 */
& [% c! d4 F3 k3 y* T( x8 S% U- s( I - HAL_ADCEx_Calibration_Start(&hadc1);//ADC自校验- a- Q- C8 D9 V2 q, `3 e
- HAL_ADC_Start_DMA(&hadc1,(uint32_t *)ADC_ConvertedData,ADC_SAMPLE);//启动AD转换并使能DMA传输和中断。ADC_ConvertedData的两个元素都是通道0的值
! z) {" I. ^) L7 p/ ~ - HAL_TIM_Base_Start(&htim6);//开启定时器6进行触发
. B( _* z# {. C7 h! y( N* U' ` - HAL_DAC_Start_DMA(&hdac1,DAC_CHANNEL_1,(uint32_t *)aEscalator8bit,6,DAC_ALIGN_8B_R);//启动DAC-DMA输出,循环输出6个8bit数 % Z" i, Q2 _1 |' I* E
- /* USER CODE END 2 */
, X. I5 f1 K4 m' y9 m- V - 1 I" I, K2 r$ e/ K$ R* g
- /* Infinite loop */3 W8 Z- R( H7 O6 G1 r
- /* USER CODE BEGIN WHILE */" t' v. W8 A& q' ]/ V2 K x
- while (1): [/ t6 W+ b' U$ k/ [( F1 m, o) S
- {
1 M" T# P1 I# D6 k r; |- N2 J; I - printf("%.2f ",((ADC_ConvertedData[0] & 0xFFF)*(3.3/4096)));//串口不是重点,这个用法可能有隐患。取有效位和换算一起进行' K0 W! J/ K) b; F. r: R
- HAL_ADC_Start(&hadc1);//因为是不连续转换,需重复开启
& `' _( w% }6 m" o! n - /* USER CODE END WHILE */
. b) A$ j V+ ^
0 p o+ i$ A3 F* N- /* USER CODE BEGIN 3 */9 Q2 T/ r8 K- e& R
- }
- `" E( r+ o* [. D - /* USER CODE END 3 */
1 e9 f* T' Z- K. ]% ?' E/ w - }
复制代码 & y" i$ e' o! R1 O0 u1 Z/ K
5 H G" K; r8 |0 `% H4 \好了,文末放下资料。
. I0 ? j) }9 b# u源代码和Cube工程:
NUCLEO-G071_DACandADC.rar
(7.98 MB, 下载次数: 1526)
|
我家里的万用表很老了,精度我这测不了。不好说不好说
DAC输出来的是近似0V的值,比如说零点零几V。缓冲的话我是开的内部缓冲。