本帖最后由 电子星辰 于 2019-1-14 17:27 编辑
( ]/ L. `( g) L4 t2 y2 Z
M+ o- G) Z& g本文在NUCLEO-G071RB核心板平台上,使用CubeMX和HAL库,通过12位ADC,连续DMA采样和不连续转换,单通道(或双通道)采集,DAC连接外部引脚通过DMA输出的一个简易正弦波。最后通过NUCLEO自带的低功耗串口1打印采集到的一个电压。6 u! [4 g+ D: Q5 v' v2 Y
5 t* M1 E5 s& U7 c1 p* E5 A+ L. @
一、Cube设置& T$ ~1 o, {; z% v4 A( d" S; ~
首先我是通过选择Board新建的工程,所以会有一个NUCLEO外设初始化的选择,可以省点力气。通过MCU新建也是一样的。
* M, ?( g) v% {0 X
& w5 n5 E$ m. Z# Z6 |! G6 {
( j3 P# b: O. v$ ]! x6 z3 @
其次,RCC、SYS、LPUART都是默认的。/ ` o d9 m0 e9 [- p- V
$ s. j! Q/ a6 T" V7 S
用SWD下载的话记得勾选“Serial Wire”,要不下载之后就不能再下载了。不过这个板子有个复位跳线帽应该好整,出了问题可以复位下载试试。9 a3 B$ z/ i1 ^. L% I2 _
6 l9 d' ?2 h: C4 N4 g! GLPUART1,异步模式自己改下常用参数就行了,“Data Direction”模式选择是我个人只需要发送。% p5 |# e* ~6 |% s3 p
1 r/ p4 [2 _9 Q
6 T& J$ P- ^7 u, X) c; T' @" z: h
然后是DAC和定时器。& g% ^( E3 P' `
DAC输出简单点,选择仅连接外部引脚就行了,另一个连接外设的选项就是连比较器之类的。需要通过定时器做触发事件(基本定时器就够了)。1 e: S: q- g( V+ I. w) y7 _
1 }" A! |4 m& ?& P+ `& UDMA通道不用管,方向是内存到外设。选循环模式,到时候打开了就不用管。“Data Width”选1个Byte就够了,方便点,不用取高低位。
7 v8 M& k J( u, d/ c2 x. @
4 |: r& L4 h8 {0 ^" a! S* _* R
定时器6是基本定时器,我只需要它一个计数触发的功能。7 m7 y. C- N: Z: B
Prescaler是定时器预分频,定时器实际时钟频率为:主频/(Prescaler+1),9 w: ~' X3 V0 t ^
Counter Period是定时器周期,当定时器开始计数到Counter Period值并且重复计数寄存器(Repetition Counter)为0时更新定时器并生成对应事件和中断,
5 q9 |6 ]0 m/ ]( h: W0 d" U0 l最终定时器频率计算为: 主频/(Prescaler+1)/(Repetition Counter+1)/(Counter Period+1(也可能不需+1)),比如需要产生20ms周期定时,可以设置为:64MHz/(63+1)/(0+1)/(19999+1)=50Hz,即20ms周期。我现在设置16位定时器的最大数,64分频的话就是65.535ms。7 ]' e6 H; |5 Z3 E% I" Q; t/ B1 W& q
: p# e# o" D( X9 k- L
/ r* L2 f" M) N# W" P接着是ADC,我写了2个通道(但是只用了其中1个)。我没有选连续转换和DMA连续请求,所以在程序里还需要循环开启ADC转换。ScanConvMode扫描转换,是根据你的转换通道数自动设置的,转换序列中如果有超过一个通道需要转换的话,那么必须开启扫描模式,否则的话,时钟只转换第一个通道。/ u' x" j" y9 A+ J1 w
7 ]5 o4 X. Q0 H. U7 tDMA通道不用管,方向从外设到内存。选的半字“Half Word”,代码里还需对取的值取低12有效位(因为上面ADC参数我是右对齐)。) V6 n# F1 r% |7 _0 J: ^9 c
* [ h4 M& p, q- ?4 j: X: t2 T
- v1 b/ H) |* b$ b' T+ M0 I5 y* |) X, I
HCLK改为64后直接回车,它会自动配置。
7 I- g% r( M+ \4 M) T( F8 o
5 S6 `' X+ X8 ] S' b
" P* S% {% W& J9 E7 S! V8 ~最后是结果,一共6个值,0.04、0.66、1.33、1.98、2.63、3.27。符合我的预期。我的输出值是0x0,0x33,0x66,0x99,0xCC,0xFF。
0 o6 O# d+ V: V H I
# i: _9 J) [1 T% V
* f H; [) K: d( k9 [2 y; u
6 K1 R g% b7 W# T; u$ d v
& b. c% f _5 B# r, V# }- /* USER CODE BEGIN PD */, y* X3 d3 s0 A4 I
- #define ADC_SAMPLE 2//采样数. T i* I& m8 ~, b7 P# B
- /* USER CODE END PD */
( a0 k9 ^8 C3 m) q2 e: I* B& E! K
, _, V- E4 Z2 v2 c o1 J- N- /* USER CODE BEGIN PV */
! j, ?2 S+ j/ B, k/ h/ z; {5 T h - __IO uint16_t ADC_ConvertedData[ADC_SAMPLE]={0};//DMA取得的ADC值
# ?, n: ?# V. K; t* p8 e - const uint8_t aEscalator8bit[6] = {0x0,0x33,0x66,0x99,0xCC,0xFF};//DAC输出量表
Z! v( z9 r! P - /* USER CODE END PV */* Z! m9 {; f3 y' X
2 e$ h6 E3 E# C2 m# ?; D- /* Private function prototypes -----------------------------------------------*/- u9 ], F: Y, } T+ r5 L" ` F
- void SystemClock_Config(void);4 z. W0 D4 V; d6 l L# Q
- /* USER CODE BEGIN PFP */
/ N2 d; T" p J, c( g: t - int main(void)
4 r5 r; J% Z" N - {6 h) y3 K+ ^# Z4 B' X& G6 H* [
- /* Reset of all peripherals, Initializes the Flash interface and the Systick. */+ p8 l: V L5 J* _% d+ \( M( ~
- HAL_Init();
2 |; T" U% E. U1 D" h B - /* Configure the system clock */: M& S0 W3 H( d3 E8 v
- SystemClock_Config();
8 e. [7 x8 I7 _. m. K - /* Initialize all configured peripherals */
7 I' C, o/ V: D0 K( _ - MX_GPIO_Init();: ~5 ]9 W8 C3 M0 u
- MX_DMA_Init();+ Q8 c4 k0 E/ h3 }$ }0 P
- MX_DAC1_Init();
) A- l1 X% P L$ m - MX_TIM6_Init();: f' m& L w. A: G# C* \ O
- MX_ADC1_Init();
1 s$ K+ M* `: m8 A6 T) Z - MX_LPUART1_UART_Init();+ Q5 D3 k* E! `4 S8 w8 ^. q& J
- /* USER CODE BEGIN 2 */
! Z' z; N' X7 Z3 L7 G8 ], L - HAL_ADCEx_Calibration_Start(&hadc1);//ADC自校验8 N! o6 F; ~2 H4 G; p
- HAL_ADC_Start_DMA(&hadc1,(uint32_t *)ADC_ConvertedData,ADC_SAMPLE);//启动AD转换并使能DMA传输和中断。ADC_ConvertedData的两个元素都是通道0的值
! d) f5 M C6 O/ k+ |/ Y4 n - HAL_TIM_Base_Start(&htim6);//开启定时器6进行触发
3 t7 F( h2 U3 h0 D. [5 k* M - HAL_DAC_Start_DMA(&hdac1,DAC_CHANNEL_1,(uint32_t *)aEscalator8bit,6,DAC_ALIGN_8B_R);//启动DAC-DMA输出,循环输出6个8bit数
# B" b" e6 @ ]) q* ~" o - /* USER CODE END 2 */
1 p! g" o9 l; D+ Y6 _
* P7 S6 e8 d" n% t Q- /* Infinite loop */# m: @' ^2 Z8 X" m5 u, m4 b
- /* USER CODE BEGIN WHILE */
% j6 C8 Q6 j4 [" O: l - while (1)! C( x9 i. T2 Y2 i, X# i% _. n
- { / ~7 B1 {( M8 p1 p! H' `2 v
- printf("%.2f ",((ADC_ConvertedData[0] & 0xFFF)*(3.3/4096)));//串口不是重点,这个用法可能有隐患。取有效位和换算一起进行 o1 ~- n, i- h# s3 f* Z* P
- HAL_ADC_Start(&hadc1);//因为是不连续转换,需重复开启, n" @$ w% @# L9 ~: ?" Q6 H
- /* USER CODE END WHILE */
6 c; q" S/ Z3 t" j
7 D* \* P# x+ d) A! S9 @- /* USER CODE BEGIN 3 */ N* N5 P/ v+ q) z5 ?
- }: j- o6 V% N( ~6 f/ t8 q3 s
- /* USER CODE END 3 */
2 o, y) j9 K2 h" ?# N - }
复制代码 ! y. h/ q' S: x- }3 r: K; ^6 p
+ e% ]! {" J X
好了,文末放下资料。
* o5 }, F5 W: {+ P7 C, k/ ~* P源代码和Cube工程:
NUCLEO-G071_DACandADC.rar
(7.98 MB, 下载次数: 1523)
|
我家里的万用表很老了,精度我这测不了。不好说不好说
DAC输出来的是近似0V的值,比如说零点零几V。缓冲的话我是开的内部缓冲。