本帖最后由 电子星辰 于 2019-1-14 17:27 编辑 3 N9 c- B# f/ \& |
8 l1 |0 g- S0 S" A; g! }* O本文在NUCLEO-G071RB核心板平台上,使用CubeMX和HAL库,通过12位ADC,连续DMA采样和不连续转换,单通道(或双通道)采集,DAC连接外部引脚通过DMA输出的一个简易正弦波。最后通过NUCLEO自带的低功耗串口1打印采集到的一个电压。5 G9 B; S/ B; q, M# B$ Z
4 N; F) ^* B# H, Q6 U6 o( Q5 }' o一、Cube设置
' x" _8 j& o# T首先我是通过选择Board新建的工程,所以会有一个NUCLEO外设初始化的选择,可以省点力气。通过MCU新建也是一样的。0 ], ~2 [3 |- M! u! y! Q* S
$ M/ t# [* M% J% N
2 S0 {* E4 S1 y m, d e: K* ?
其次,RCC、SYS、LPUART都是默认的。
. ^+ x; b, E6 y9 v
8 c D* u0 q0 _3 ] E/ o用SWD下载的话记得勾选“Serial Wire”,要不下载之后就不能再下载了。不过这个板子有个复位跳线帽应该好整,出了问题可以复位下载试试。6 n4 }; j. `# L( y# N3 H- Q
. z% H! K$ [4 l/ I* h( E) z! e
LPUART1,异步模式自己改下常用参数就行了,“Data Direction”模式选择是我个人只需要发送。+ [. k. ]5 J7 y/ z7 W% A/ f- C
+ d" A y6 V! Z3 v! C/ M8 S+ B$ ^% w
然后是DAC和定时器。4 {, {8 u* q% b" Z2 e
DAC输出简单点,选择仅连接外部引脚就行了,另一个连接外设的选项就是连比较器之类的。需要通过定时器做触发事件(基本定时器就够了)。
, E: ]. u" N' p5 U, q4 _
' e( Z0 M# i$ y- A( @6 ~, NDMA通道不用管,方向是内存到外设。选循环模式,到时候打开了就不用管。“Data Width”选1个Byte就够了,方便点,不用取高低位。3 u0 m: G, Y4 ]6 m! d; q
9 l6 @; ?' g9 Q5 U7 f( h# x m6 N" }定时器6是基本定时器,我只需要它一个计数触发的功能。
4 L; `6 x* {, T; Y' ePrescaler是定时器预分频,定时器实际时钟频率为:主频/(Prescaler+1),
7 k6 A4 Z- M( d0 C8 l* [5 f RCounter Period是定时器周期,当定时器开始计数到Counter Period值并且重复计数寄存器(Repetition Counter)为0时更新定时器并生成对应事件和中断,
4 n8 V7 W3 o: A2 \. u最终定时器频率计算为: 主频/(Prescaler+1)/(Repetition Counter+1)/(Counter Period+1(也可能不需+1)),比如需要产生20ms周期定时,可以设置为:64MHz/(63+1)/(0+1)/(19999+1)=50Hz,即20ms周期。我现在设置16位定时器的最大数,64分频的话就是65.535ms。0 I! q( v; d" w+ t4 D( c
; I) r# {8 N& ]& N+ W- k9 g0 U+ s( `
9 g; i6 E9 x- S% L) c- Y接着是ADC,我写了2个通道(但是只用了其中1个)。我没有选连续转换和DMA连续请求,所以在程序里还需要循环开启ADC转换。ScanConvMode扫描转换,是根据你的转换通道数自动设置的,转换序列中如果有超过一个通道需要转换的话,那么必须开启扫描模式,否则的话,时钟只转换第一个通道。
, [, ?( j! h( ]$ A4 N
- m( z" C. @# J3 F/ p c: l
DMA通道不用管,方向从外设到内存。选的半字“Half Word”,代码里还需对取的值取低12有效位(因为上面ADC参数我是右对齐)。
- }$ {5 @, m6 ]- S3 v3 g
+ d* A* @1 n6 ~4 W
7 P; v1 V' r7 f0 |" Y- d/ _! E1 B9 ~; X: o& Q. D0 u
HCLK改为64后直接回车,它会自动配置。
. d" d+ i: K+ o8 w9 `' q% e
* V" q+ y5 @4 L$ O, y
$ V+ ^9 d* i; H2 h: W6 ]最后是结果,一共6个值,0.04、0.66、1.33、1.98、2.63、3.27。符合我的预期。我的输出值是0x0,0x33,0x66,0x99,0xCC,0xFF。
0 Z( @1 X+ k _- K
0 y2 v$ G7 g5 ?
0 j. P8 F4 h- c x% c7 L" u9 a- y3 f
8 N% [+ U6 f4 w9 U7 Q
- /* USER CODE BEGIN PD */$ g7 Y& x% g9 A* I9 `# q
- #define ADC_SAMPLE 2//采样数2 D: y8 y6 t& h8 o* D
- /* USER CODE END PD */& _( r( H3 r* X" `: S
- / Y5 Y9 q5 ~1 I2 \4 C
- /* USER CODE BEGIN PV */
4 q' x8 v; k' i# K8 G0 y - __IO uint16_t ADC_ConvertedData[ADC_SAMPLE]={0};//DMA取得的ADC值; _! h; l* z, i# F/ Z) v) w
- const uint8_t aEscalator8bit[6] = {0x0,0x33,0x66,0x99,0xCC,0xFF};//DAC输出量表0 F; a4 m1 ?2 }% { K
- /* USER CODE END PV */0 Y( R9 f& k* K; u* S# k
- t% F H7 r4 I- V8 {! P- d
- /* Private function prototypes -----------------------------------------------*/
6 v* r# S! a4 N3 U' r3 _) s0 ~ - void SystemClock_Config(void);
; I$ A& [# s2 v- _# h$ Y - /* USER CODE BEGIN PFP */. |, n' }3 K8 S8 k. i' E" F! V
- int main(void)
w; v1 a# t) O7 V - {' C' i* t* g6 a" V/ C; u3 g5 ~1 E1 g2 V
- /* Reset of all peripherals, Initializes the Flash interface and the Systick. */5 Y5 \9 [, G9 i6 s' J3 P
- HAL_Init();; s+ h2 u& v3 c" y+ X4 K+ T# t# e0 X
- /* Configure the system clock */% S1 a( g2 Q/ \: C5 W
- SystemClock_Config();
" L5 n4 M* k/ c: @/ I7 _5 O - /* Initialize all configured peripherals */
* Z& e/ H/ L' T" b8 a- v6 a - MX_GPIO_Init();
* D+ K+ o: |/ b8 X# r - MX_DMA_Init();
& Q4 |, ?" W5 _ - MX_DAC1_Init();
! b2 D6 X/ E) n( Q9 a - MX_TIM6_Init();! v/ j2 ?8 ?! f0 H) _' p
- MX_ADC1_Init();* ^- w! |% q5 {, P
- MX_LPUART1_UART_Init();" K8 o; L& M p
- /* USER CODE BEGIN 2 */# v7 \2 ~! r2 _: ^( ]3 `
- HAL_ADCEx_Calibration_Start(&hadc1);//ADC自校验
2 E" d- T. @+ v# e. u0 y - HAL_ADC_Start_DMA(&hadc1,(uint32_t *)ADC_ConvertedData,ADC_SAMPLE);//启动AD转换并使能DMA传输和中断。ADC_ConvertedData的两个元素都是通道0的值
4 a8 f: u7 D, A# t - HAL_TIM_Base_Start(&htim6);//开启定时器6进行触发. t5 G" V( _5 f6 t* }0 _9 ^
- HAL_DAC_Start_DMA(&hdac1,DAC_CHANNEL_1,(uint32_t *)aEscalator8bit,6,DAC_ALIGN_8B_R);//启动DAC-DMA输出,循环输出6个8bit数 - ]* ?" j% p# i
- /* USER CODE END 2 */+ K. z6 X6 c: Y' Z5 k% t) j( R3 E. ?
2 |( |% o4 w1 s1 ]1 e1 D3 Q4 B- /* Infinite loop */9 K4 S, Z: |( @5 U3 \
- /* USER CODE BEGIN WHILE */6 ^/ F7 s, b9 m3 [3 v7 {
- while (1)& \$ S% I. V H" T* I- R
- {
+ c$ b, m C0 I# |6 r - printf("%.2f ",((ADC_ConvertedData[0] & 0xFFF)*(3.3/4096)));//串口不是重点,这个用法可能有隐患。取有效位和换算一起进行
. C3 |2 I, ~' p- I1 r - HAL_ADC_Start(&hadc1);//因为是不连续转换,需重复开启+ l( n0 h: w# X$ `
- /* USER CODE END WHILE */
- [0 P" `+ Y {( W' G! Y - $ K0 S% Y* C# Z* _
- /* USER CODE BEGIN 3 */
% M! p# @* `1 e m5 Y - }
% F; d5 [$ E1 l2 b% d D - /* USER CODE END 3 */6 R* N Q; F) |3 ]+ d2 g& Y, T$ P
- }
复制代码
5 z& s& a, \% X1 f" G. Q. f. {, F* Q( F& q/ w* n
好了,文末放下资料。
0 d0 M7 x" y" B, e* a# r( Q" A源代码和Cube工程:
NUCLEO-G071_DACandADC.rar
(7.98 MB, 下载次数: 1524)
|
我家里的万用表很老了,精度我这测不了。不好说不好说
DAC输出来的是近似0V的值,比如说零点零几V。缓冲的话我是开的内部缓冲。