你的浏览器版本过低,可能导致网站不能正常访问!
为了你能正常使用网站功能,请使用这些浏览器。

【学习指南】基于STM32G474VET6 开发板实验经验分享(二)

[复制链接]
攻城狮Melo 发布时间:2024-11-22 15:27
六、SPI 实验
: y4 G8 o6 j: _& e( Q9 c实验目的:掌握和熟悉 SPI 软件模拟和硬件控制的使用和配置方法。) r+ x. Y- R6 Y4 [; G
/ W( q4 _/ u+ N  Q9 k: a
1、软件模拟 SPI 驱动 TFT 实验: A" A6 _: q2 x# b5 F
CubeMX 配置如下,保存后生成对应的配置代码:
  ?$ l3 T0 Y  l3 r# H/ N
13.png
( O6 F+ L5 q) A3 d7 b

( _+ ]0 F( d8 ?+ B, b
▲ CubeMX 配置
- n3 K" n/ E1 h# s. q! e
本实验使用软件模拟 SPI,只需要对相应 IO 进行配置即可,注意需要配置 IO 速度等级,CLK 信号和 SDA 信号频率较高,需要配置为 very high。
3 M: A0 d8 @& b) V0 f4 u5 H. X$ e$ s  X& h- m6 @
相关操作函数说明:5 S* g* ^  x0 f/ |/ z
void Lcd_Reset(void)
" ^& l+ f# W: K  `

( S0 V+ U' c8 a7 r/ Q功能:液晶硬复位函数;9 \. Q- N) U8 e. U1 R
参数:无;. k+ Z8 F0 W& A. K3 A
返回:无;" }, P/ |! d7 d; o, R
说明:液晶初始化前需执行一次复位操作
; d: L( o" M& e4 Jvoid LCD_Initial(void)
9 @' |3 v& V7 B) m' b功能:初始化液晶;
$ M3 A- t9 J7 M: ]* W) Q参数:无;) z: }8 U/ W) z: d0 E* G: @
返回:无;+ P0 `5 Z0 |6 c" Q
说明:在对液晶写入内容前需要进行初始化配置;
3 M) C) U! l# P# }& z' W8 d# wvoid Lcd_ColorBox(unsigned int xStart,unsigned int yStart,unsigned int xLong,unsigned int yLong,unsigned int Color)
6 X3 W$ z) G+ f6 S" o% D) M
& v* F3 o5 S( T9 u/ F, n
功能:Lcd 矩形填充函数;
. Q3 j! r$ Z4 D7 y参数 1:x 方向的起始点;
- C3 H+ N8 t, i5 [# G参数 2:y 方向的起始点;
. [0 W- g6 Q. A1 K: O7 W参数 3:x 方向的长度;% V2 f& y) w) d1 d3 q- H; B$ j
参数 4:y 方向的长度;
/ h: Q; Z  B: `' N参数 5:填充的颜色;% ^, H% M  m9 H6 Z9 Q1 X3 V
返回:无;
; p+ b7 L1 Q& p6 }& z# w% h说明:将指定区域内填充指定颜色,常用于清屏# h: M& Q' h* W# [3 t6 ]1 u
void BlockWrite(unsigned int Xstart,unsigned int Xend,unsigned int Ystart,unsigned int Yend)$ O! @: B& {3 B1 ~5 C8 x

+ g* t/ S; s: R4 F* c功能:在一个指定位置开一个矩形框;
4 H" V) f  v/ e- }: ]% u0 i: c! e参数 1:x 方向的起始点;, G3 ?4 @1 ~! C% X. Q2 O) I- m
参数 2:x 方向的终点;9 g' ]2 ?1 Q2 ^0 n$ X
参数 3:y 方向的起始点;* o- o7 x0 W0 Q+ K8 K
参数 4:y 方向的终点;
/ }' Y# y2 ]3 o. I, S4 r$ ?! [* l返回:无;
! w; g8 w1 U+ w, i9 F: q说明:开一个矩形框,方便接下来往这个框填充数据;3 A" d0 U9 p4 H7 ]
void DrawPixel(unsigned int x, unsigned int y, int Color)
% W; `6 u9 D8 O6 W- N- F9 s1 u功能:在 x,y 坐标上打一个颜色为 Color 的点;
1 y* B+ m" \7 n- q; Q, A参数 1:x 坐标;2 v5 N; m2 c% L8 x* w; u/ {! |
参数 2:y 坐标;
, x% M* \7 V6 D4 G7 F9 k参数 3:点的颜色;
& T5 P. {* _+ t  g: ]2 j7 @6 M1 c返回:无;9 F5 T  O% L+ b' J" f8 T0 u/ C
void LCD_PutString(unsigned short x, unsigned short y, char *s, unsigned int fColor, unsigned int bColor,unsigned char flag)
% V# Q+ A) O. s' _% o- R) t
* n8 [( k4 g0 \1 @4 r9 b, @功能:显示一个字符串;
+ J. S1 z( O8 x, S1 G) O参数 1:起始点 x 坐标;
  G; g5 z( r0 W; e4 X. i1 {参数 2:起始点 y 坐标;; M8 D3 N3 Z4 ^- @4 m1 U
参数 3:字符串指针;" }% n5 ^  \2 q' M1 J2 _7 I
参数 4:前景色;: e) C4 z! O8 ]1 M/ ?4 B
参数 5:背景色;; A2 j3 Y8 i6 O. b$ G" R
参数 6:有无背景色;
6 H4 s/ ?) x" e9 \& B$ w返回:无
" n4 |( o0 V1 W' p" |; {2 N4 _  \& o5 s+ t/ h+ I: i5 i

' g; `& K0 ^; H  [4 \4 p核心代码:2 U) ]0 h: M% [; Y* f9 C
LCD_Initial();
' g& J( h, t2 gLcd_ColorBox(0,0,XSIZE_PHYS,YSIZE_PHYS,Blue);//用蓝色清屏
. Q& \7 [" Z1 Y/ k/ Z" x- MLcd_ColorBox(0,0,XSIZE_PHYS,YSIZE_PHYS,Red);//用红色清屏$ B$ u) u. J* A7 A7 a% s
Lcd_ColorBox(0,0,XSIZE_PHYS,YSIZE_PHYS,Green);//用绿色清屏* E$ o# J1 |) T
Lcd_ColorBox(0,0,XSIZE_PHYS,YSIZE_PHYS,White);//用白色清屏
7 y7 ?' \3 Z" M; f- Z& iLCD_PutString(10,10,"STM32G474Test",Red,White,0);//显示字符4 K1 A3 R; C* ^% w' L4 ]
' I" p5 N/ q2 r
在 main 函数中进行过外设初始化之后,对 LCD 进行初始化,然后分别用四种颜色清屏,最后显示测试字符。$ h5 U) p6 @6 ]2 _5 t$ }  g! ]

9 X7 x- H; |. f% K6 X! O1 _
# u9 G9 o* a4 J! N2 U; ]
实验现象:) q; S" a7 b5 ?/ }, j. R
下载烧录后可以观察到屏幕分别刷新蓝红绿白四种颜色,最后显示测试字符STM32G474Test。
2 b' }8 n; _# W# k% A. L3 h! v7 m" u, q0 `! m
3 X/ K4 O- k- P. c5 `& I
2、硬件 SPI 驱动 TFT 实验, `# T1 g+ S' h/ W9 @" u% \
CubeMX 配置如下,保存后生成对应的配置代码:5 w% \7 c9 Q$ ~$ _+ D0 \4 |$ c

7 _7 @5 n# }5 w1 U
12.png
+ H; j* W' O0 d& S; q& R$ ]! h

$ N- _1 u: V, Q0 x
▲ CubeMX 进行 SPI 配置
微信图片_20241122152531.png
( z* Y7 v- s5 T
; }! J% X% h) c% B0 `; @
▲ CubeMX 进行 IO 速度配置7 L; x4 G1 i5 B- h" Q3 z, s
本实验使用硬件 SPI,需要配置 SPI 的时钟分频,配置出合适的时钟速率,另外需要注意设置时钟信号的空闲电平以及采样边沿,还需要将高速的信号 IO 速度进行配置,其他 IO配置与软件模拟 SPI 相同。
* A$ C0 H; x) F1 t" T

$ E8 {  V. O$ g

4 O2 i7 g' ], o  v8 k0 p" G5 S
& s" F* M# ~( G6 s+ ]" r; z
相关操作函数说明:
9 h6 d  s! q7 Y, l9 Q2 ^
HAL_StatusTypeDef HAL_SPI_Transmit(SPI_HandleTypeDef *hspi, uint8_t *pData, uint16_t Size, uint32_t Timeout)

; |2 q/ v2 J7 w3 O$ C! V
功能:通过硬件 SPI 发送一组数据;
& a2 y" `8 r  B' @% h( b; n
参数 1:SPI 句柄,根据实际需要填写;

4 q7 g2 Y4 ^* o5 G# ^) R9 Z) ]
参数 2:要发送数据的指针,常见为发送数据数组的首地址;

" d6 \' H% \# M) D
参数 3:发送数据长度,单位字节;
+ W+ g+ U: ?7 [+ f+ i  `# q
参数 4:发送超时时间,单位 ms;

& h3 B7 ^# q3 o$ [5 I
返回:操作结果,HAL_OK,HAL_ERROR;
7 h3 G8 g8 J' {# D  n
示例:HAL_SPI_Transmit ( &hspi4,data_color,2*xLong,10 );//通过 SPI4 发送颜色数据
) W' M! X% ~+ J1 e$ h
HAL_StatusTypeDef HAL_SPI_Receive(SPI_HandleTypeDef *hspi, uint8_t *pData, uint16_t Size, uint32_t Timeout)

' [# E; m; K& `4 |% j" E
功能:通过硬件 SPI 接收一组数据;

1 j1 G* r# a+ {3 \6 s8 i* g
参数 1:SPI 句柄,根据实际需要填写;
4 o& T) s; y& C: G% m/ n
参数 2:要接收数据保存指针;
8 u& N+ z. I# b+ ]
参数 3:接收数据长度,单位字节;

( s5 [' `9 g$ Q$ N- j
参数 4:接收超时时间,单位 ms;
$ n6 g, M0 p, s
返回:操作结果,HAL_OK,HAL_ERROR;

: A! b, n3 Z) W0 K
HAL_StatusTypeDef HAL_SPI_TransmitReceive(SPI_HandleTypeDef *hspi, uint8_t *pTxData, uint8_t *pRxData, uint16_t Size, uint32_t Timeout)

4 A- ]) E$ ^# P# g2 Y
功能:通过硬件 SPI 交换一组数据;
" ~8 o$ w- y  w8 B% M" c- y
参数 1:SPI 句柄,根据实际需要填写;

. f5 t$ H0 V# ?7 l
参数 2:要发送数据的指针,常见为发送数据数组的首地址;

) {. R$ T1 I% Z+ m4 x) W
参数 3:要接收数据的指针,接收数据数组的首地址;

. Y+ q# J7 U/ V3 W/ o, h; ~
参数 4:数据长度,单位字节;

* S: s* N% y1 g: ~2 }* R4 z7 P0 {
参数 5:超时时间,单位 ms;

1 v5 n; g. x4 Q$ T
返回:操作结果,HAL_OK,HAL_ERROR;

+ I4 z" Y9 I9 v! p) [' |7 \8 n* g

9 k2 B% l$ q* {2 W, P8 ^. T# O

6 ~8 x8 w' w* B: b
核心代码:

4 D+ l+ A9 t$ A7 j7 B2 b2 B& u* j% S8 ^. F
LCD_Initial();

+ E1 y5 |- Q5 \% m
Lcd_ColorBox(0,0,XSIZE_PHYS,YSIZE_PHYS,Blue);//用蓝色清屏

& p+ j8 K" S9 T* C. K
Lcd_ColorBox(0,0,XSIZE_PHYS,YSIZE_PHYS,Red);//用红色清屏

6 Y5 `! x" e" s3 j- Z
Lcd_ColorBox(0,0,XSIZE_PHYS,YSIZE_PHYS,Green);//用绿色清屏

7 T5 h/ m" m+ }& ~! R/ E3 v4 I
Lcd_ColorBox(0,0,XSIZE_PHYS,YSIZE_PHYS,White);//用白色清屏

7 d# h7 p- ~1 I) ~3 X
LCD_PutString(10,10,"STM32G474Test",Red,White,0);//显示字符
) v8 v$ s3 B/ V: f0 Q1 z  T
+ F9 \& s; V& n, H/ j  M, ]8 a
7 a) A8 |3 D! \+ L8 F2 ?
在 main 函数中进行过外设初始化之后,对 LCD 进行初始化,然后分别用四种颜色清屏,最后显示测试字符。
* m: U3 e9 C6 r, m6 g
6 w& j4 d+ k7 l/ ?1 X' r: r

4 `/ a, \- I- r+ ]
实验现象:

' I, ?; N/ t7 G! z
下载烧录后可以观察到屏幕分别刷新蓝红绿白四种颜色,最后显示测试字符STM32G474Test。
/ N8 {- C7 [6 `; b; g
- y1 f8 t0 F% n9 |, ?2 R- g
0 [5 [) r0 r9 ^( L4 q% \
3、硬件 SPI 驱动 TFT 实验(DMA)
4 s9 V  W' K* F2 e7 l  D
CubeMX 配置如下,保存后生成对应的配置代码:
微信图片_20241122152534.png
* ]: f. [1 p8 o; j) U/ D8 Y9 R) H/ w9 W
▲ CubeMX 进行 SPI 的 DMA 配置
本实验使用硬件 SPI,使用 DMA 进行发送。

5 [$ h/ a- A8 Y* \7 j( ?3 S7 d+ Z( W
相关操作函数说明:
HAL_StatusTypeDef HAL_SPI_Transmit_DMA(SPI_HandleTypeDef *hspi, uint8_t *pData,uint16_t Size)
功能:通过硬件 SPI 使用 DMA 方式发送一组数据;
参数 1:SPI 句柄,根据实际需要填写;
参数 2:要发送数据的指针,常见为发送数据数组的首地址;
参数 3:发送数据长度,单位字节;
返回:操作结果,HAL_OK,HAL_ERROR;
示例:HAL_SPI_Transmit_DMA ( &hspi4,data_color,2*xLong );//通过 SPI4 的 DMA 方式发送颜色数据
HAL_StatusTypeDef HAL_SPI_Receive_DMA(SPI_HandleTypeDef *hspi, uint8_t *pData,uint16_t Size)
  D: R& }+ R1 Z
功能:通过硬件 SPI 的 DMA 方式接收一组数据;
参数 1:SPI 句柄,根据实际需要填写;
参数 2:要接收数据保存指针;
参数 3:接收数据长度,单位字节;
返回:操作结果,HAL_OK,HAL_ERROR;
HAL_StatusTypeDef HAL_SPI_TransmitReceive_DMA(SPI_HandleTypeDef *hspi, uint8_t*pTxData, uint8_t *pRxData, uint16_t Size)

- ]! y0 F# |2 H, F+ v
功能:通过硬件 SPI 的 DMA 方式交换一组数据;
参数 1:SPI 句柄,根据实际需要填写;
参数 2:要发送数据的指针,常见为发送数据数组的首地址;
参数 3:要接收数据的指针,接收数据数组的首地址;
参数 4:数据长度,单位字节;
返回:操作结果,HAL_OK,HAL_ERROR;
注意:使用相应 DMA 时需要对该 DMA 请求进行配置;

/ ?  t* B* c9 {! e, }1 F
核心代码:
//发送函数修改
if((temp+1) % xLong == 0)
{
HAL_SPI_Transmit_DMA(&hspi4,data_color,2*xLong);
while(!dma_flag_temp);
dma_flag_temp = 0;
}

: J# m  R1 Z6 {
使用 DMA 方式进行发送时需要确保上一次 DMA 发送已经完成,要避免重复请求。
! k: X3 I8 a7 O9 M8 n* V; _
void DMA1_Channel1_IRQHandler(void)
{
if(__HAL_DMA_GET_FLAG(&hdma_spi4_tx,DMA_FLAG_TC1))
{
dma_flag_temp=1;
__HAL_DMA_CLEAR_FLAG(&hdma_spi4_tx,DMA_FLAG_TC1);
HAL_SPI_DMAStop(&hspi4);
}
HAL_DMA_IRQHandler(&hdma_spi4_tx);
}
4 v* R1 H/ u8 {% P& \3 T" v
在 DMA 中断中判断是否发生了 DMA 传输完成事件,如果 DMA 传输完成则将相应标志位置位,并清除标志。
* M' l1 d* H$ L
LCD_Initial();Lcd_ColorBox(0,0,XSIZE_PHYS,YSIZE_PHYS,Blue);//用蓝色清屏
Lcd_ColorBox(0,0,XSIZE_PHYS,YSIZE_PHYS,Red);//用红色清屏
Lcd_ColorBox(0,0,XSIZE_PHYS,YSIZE_PHYS,Green);//用绿色清屏
Lcd_ColorBox(0,0,XSIZE_PHYS,YSIZE_PHYS,White);//用白色清屏
LCD_PutString(10,10,"STM32G474Test",Red,White,0);//显示字符

. x5 }' ^. C( F$ B( Y( S
在 main 函数中进行过外设初始化之后,对 LCD 进行初始化,然后分别用四种颜色清屏,最后显示测试字符。
* R8 r& U+ H# M& @
实验现象:
下载烧录后可以观察到屏幕分别刷新蓝红绿白四种颜色,最后显示测试字符STM32G474Test。
# H7 O6 P0 z! A3 U: f& o
' p: r- I' R% O. a" D+ f! `- T
七、IIC实验

! _8 _$ ^: v- B" U4 R4 i; G6 t
实验目的:掌握和熟悉 IIC 软件模拟和硬件控制的使用和配置方法。

5 N9 P; v* T, y9 ]1 r3 }7 ?9 O' R
1、软件模拟 IIC 驱动 24C02 实验
CubeMX 配置如下,保存后生成对应的配置代码:

0 @: @6 f. Y+ O0 r( S
4.png
2 |  l- x$ |9 w' @" r4 s

: B) X7 c- L0 a+ u, h
▲ CubeMX 进行软件 IIC 的 IO 配置

9 w. b1 ~6 e/ X" p
本实验使用软件 IIC 模拟,只需要配置 IO,初始 IO 配置都配置为输出 IO 即可,24C02 外围电路有上拉电阻,不需要配置内部上拉。

6 Y5 }2 R" y& K. _! N0 ~  f
相关操作函数说明:
void SDA_Input_Mode()
功能:将 SDA 切换到输入模式;
参数:无;
返回:无;
说明:SDA 是双向的,在进行数据读取时需要切换到输入模式

/ C8 R) R* R8 k8 ~, t
void SDA_Output_Mode()
功能:将 SDA 切换到输出模式;
参数:无;
返回:无;
说明:SDA 是双向的,在进行数据发送时需要切换到输出模式
1 b, y* v& j2 X
void I2CStart(void)
功能:模拟 IIC 的起始信号;
参数:无;
返回:无;
! _, z9 r+ z. u# i# j9 T
void I2CStop(void)
功能:模拟 IIC 的停止信号;
参数:无;
返回:无;

  c$ c+ N- D8 w; j6 V) ?; \3 ?
unsigned char I2CWaitAck(void)
功能:模拟 IIC 等待应答;
参数:无;
返回:应答结果,ERROR 或 SUCCESS;
- ], L' J  f, Y8 m3 W
void I2CSendAck(void)
功能:模拟 IIC 的应答信号;
参数:无;
返回:无;
, v# I& e3 U0 e7 e
void I2CSendNotAck(void)
功能:模拟 IIC 的非应答信号;
数:无;
返回:无;

% @: l% ^7 p7 D$ j% w
void I2CSendByte(unsigned char cSendByte)
功能:通过模拟 IIC 发送一个字节;
参数:需要发送的字节;
返回:无;

$ b% J6 I3 z" d
unsigned char I2CReceiveByte(void)
功能:通过模拟 IIC 接收一个字节;
参数:无;
返回:接收到的字节;
7 D: f$ p$ l$ X- X! C. C
核心代码:
//24C02 读取一个字节
uint8_t x24c02_read(uint8_t address)
{
unsigned char val;
I2CStart();//起始信号
I2CSendByte(0xa0);//发送器件写地址
I2CWaitAck();//等待应答
I2CSendByte(address);//发送读取的内存地址
I2CWaitAck();//等待应答
I2CStart();//起始信号
I2CSendByte(0xa1);//发送器件读地址
I2CWaitAck();//等待应答
val = I2CReceiveByte();//接收一个字节
I2CWaitAck();//等待应答
I2CStop();//停止信号
return(val);
}
//24C02 读取写入一个字节
void x24c02_write(uint8_t address, uint8_t info)
{
I2CStart();//起始信号
I2CSendByte(0xa0);//发送器件写地址
I2CWaitAck();//等待应答
I2CSendByte(address);//发送写入的内存地址
I2CWaitAck();//等待应答
I2CSendByte(info);//发送写入内容
I2CWaitAck();//等待应答
I2CStop();//停止信号
}

4 N" \; ?3 ?- q  d. g7 E0 x$ e
上述两个函数为 24C02 的读写函数,写器件地址为 0xA0,读器件地址为 0xA1,地址由外部电路连接决定。

  G) F0 p7 t, W- G' u* s( E
I2CInit();
uint32_t i;

5 D8 A- S9 u6 m$ N7 d1 b6 t
printf(" 24C02 Test ....\r\n\r\n");
//向 0x00 内存地址写入数据
for(i = 0; i < 6; i++)
{
x24c02_write(i,Data_T);
}
printf(" 24C02 Write ok\r\n");
HAL_Delay(100);
7 r( L7 H* D( Y6 x" b! ?6 {/ A
//从 0x00 内存地址读出数据
for(i = 0; i < DataSize; i++)
Data_R=x24c02_read(i);
printf(" 24C02 Read ok\r\n");
) f8 ~* O; ]) {; E
printf("24C02 Read Data : \r\n");
for(i = 0; i < DataSize; i++)
printf("0x%02X ", Data_R);
printf("\r\n\r\n");
if(memcmp(Data_T, Data_R, DataSize) == 0)
{
printf(" 24C02 Test OK\r\n");
}
else
{
printf(" 24C02 Test Failed\r\n");
}

* i* K3 @/ R( ~: I
以上为 main 函数中外设初始化结束后的部分,通过软件模拟 IIC 向 24C02 内存地址写入一段设定好的数据,然后将这段数据读出,最后进行对比。
$ O/ J( W. a, m% m- L
实验现象:
下载烧录后可以观察到上位机串口助手打印测试数据。
7 j3 W5 h9 C$ a/ f& }/ E; @
微信图片_20241122152540.png
8 u, w6 Q3 u( K  n, i6 ^

  T, o8 Y! I7 }; V) A6 w
▲ 实验现象

7 a/ Y* @1 I) X! f; q& `

, y2 O; ~/ r& d5 Y
2、硬件 IIC 驱动 24C02 实验

1 \, T6 B0 h) C4 m; i
CubeMX 配置如下,保存后生成对应的配置代码:

! H5 ?& F# w' y  [. n* |- T. f( ?
微信图片_20241122152543.png
6 u. v& }: F6 Q- w% X

+ _/ }) C# Z$ P- W4 H. O
▲ CubeMX 进行 IIC 配置
8 ?2 y* f; t. I( {4 f( g7 D6 L
本实验使用硬件 IIC,启用之后 IIC 的配置不需要改变。
2 v4 u9 g( h" ]$ r/ R) d
相关操作函数说明:
HAL_StatusTypeDef HAL_I2C_Mem_Write(I2C_HandleTypeDef *hi2c, uint16_t DevAddress,uint16_t MemAddress,uint16_t MemAddSize, uint8_t *pData, uint16_t Size, uint32_t Timeout)
功能:以阻塞方式将一定量的数据写入指定的内存地址;
参数 1:I2C 句柄,根据实际需要填写;
参数 2:设备地址,注意这里填入的地址应该是左移一位之后的地址;
参数 3:目标内存的地址;
参数 4:目标内存的地址大小,可选 8 位(I2C_MEMADD_SIZE_8BIT),16 位(I2C_MEMADD_SIZE_16BIT);
参数 5:带发送数据的指针;
参数 6:待发送的数据量;
参数 7:发送超时时间;
返回:操作结果,HAL_OK,HAL_ERROR;

+ c2 e  q2 |& y; o4 {
示例:
HAL_I2C_Mem_Write(&hi2c3,Addr_W,0x01,I2C_MEMADD_SIZE_8BIT,Data_T,DataSize,0xFF);//通过 IIC 向目标器件的 0x01 地址写入待发送数据;
1 o# P" M. V! }& K) p9 v3 b
HAL_StatusTypeDef HAL_I2C_Mem_Read(I2C_HandleTypeDef *hi2c, uint16_t DevAddress,uint16_t MemAddress,uint16_t MemAddSize, uint8_t *pData, uint16_t Size, uint32_t Timeout)
功能:通过硬件 IIC 从一个特定的内存地址以阻塞模式读取一定量的数据;
参数 1:I2C 句柄,根据实际需要填写;
参数 2:设备地址,注意这里填入的地址应该是左移一位之后的地址;
参数 3:目标内存的地址;
参数 4:目标内存的地址大小,可选 8 位(I2C_MEMADD_SIZE_8BIT),16 位(I2C_MEMADD_SIZE_16BIT);
参数 5:带接收数据保存地址的指针;
参数 6:待接收的数据量;
参数 7:接收超时时间;
返回:操作结果,HAL_OK,HAL_ERROR,HAL_BUSY;
示例:
HAL_I2C_Mem_Read(&hi2c3,Addr_R,0x01,I2C_MEMADD_SIZE_8BIT,Data_R,DataSize,0xFF);//通过 IIC 从目标器件的 0x01 地址读取数据;
0 N3 S8 E" ~8 ?5 a0 i

% x8 ?* u' ]9 L  j1 Z
核心代码:
uint32_t i;
printf(" 24C02 Test ....\r\n\r\n");
//向 0x01 内存地址写入数据
HAL_I2C_Mem_Write(&hi2c3,Addr_W,0x01,I2C_MEMADD_SIZE_8BIT,Data_T,DataSize,0xFF);
printf(" 24C02 Write ok\r\n");
HAL_Delay(100);
//从 0x01 内存地址读出数据
HAL_I2C_Mem_Read(&hi2c3,Addr_R,0x01,I2C_MEMADD_SIZE_8BIT,Data_R,DataSize,0xFF);
printf(" 24C02 Read ok\r\n");
printf("24C02 Read Data : \r\n");
for(i = 0; i < DataSize; i++)
printf("0x%02X ", Data_R);
printf("\r\n\r\n");
if(memcmp(Data_T, Data_R, DataSize) == 0)
{
printf(" 24C02 Test OK\r\n");
}
else
{
printf(" 24C02 Test Failed\r\n");
}

: ^# v; O+ C. `$ @2 i
以上为 main 函数中外设初始化结束后的部分,通过硬件 IIC 向 24C02 内存地址写入一段设定好的数据,然后将这段数据读出,最后进行对比。

. [! K' A9 ?) N* I  L* `
实验现象:
下载烧录后可以观察到上位机串口助手打印测试数据。
; A$ K9 }' {/ a  P1 V, c& C3 W
微信图片_20241122152546.png / L' \& O$ S4 |! l# f
▲ 实验现象

4 O2 ?; B# u; d9 I- U

5 T/ r- y& i! C" b0 N( Z
七、ADC实验

; }% _! G" {" C
实验目的:掌握和熟悉 ADC 单路采集和多路采集的使用和配置方法,包含查询,中断,DMA等方式。

, J0 F' }8 X0 \; J: d/ V' {
1、ADC 查询方式单路采集实验
* c  E+ A& ~6 G
CubeMX 配置如下,保存后生成对应的配置代码:

% @5 d& q* ?+ l. O4 D4 m; t5 Z
微信图片_20241122152549.png
▲ CubeMX 进行 ADC 配置

6 z2 i* _; Y; E- n, a8 o- S/ O
本实验进行单通道 ADC 软件触发采样,只需要对 ADC 进行简单配置即可,同时使用串口进行数据输出,串口与时钟系统配置上文已经展示,参照上文实验进行配置。
% q; m+ E% I$ u% z8 F8 ^3 @
相关操作函数说明:
HAL_StatusTypeDef HAL_ADCEx_Calibration_Start(ADC_HandleTypeDef *hadc, uint32_t SingleDiff)
功能:对 ADC 进行校准;
参数 1:ADC 句柄,根据实际需要填写;
参数 2:ADC 采样模式,可选 ADC_DIFFERENTIAL_ENDED(差分采样模式)或ADC_SINGLE_ENDED(单端采样模式);
返回:操作结果,HAL_OK,HAL_ERROR;
示例:HAL_ADCEx_Calibration_Start(&hadc1,ADC_SINGLE_ENDED); //对 ADC1 进行单端采样模式下的校准;
3 H  F8 }8 Z. r5 F/ o! q
HAL_StatusTypeDef HAL_ADC_Start(ADC_HandleTypeDef *hadc)
功能:使能 ADC,开启 ADC 规则组转换;
参数 1:ADC 句柄,根据实际需要填写;
返回:操作结果,HAL_OK,HAL_ERROR,HAL_BUSY;
示例:HAL_ADC_Start(&hadc1); //开启 ADC1 转换
注意:如果不是工作在连续模式,运行一次该函数进行一次转换
4 p2 N% l' \5 e9 w* e  B/ B
HAL_StatusTypeDef HAL_ADC_Stop(ADC_HandleTypeDef *hadc)
功能:关闭 ADC,停止 ADC 规则组转换;
参数 1:ADC 句柄,根据实际需要填写;
返回:操作结果,HAL_OK,HAL_ERROR;

: _5 I2 Z9 N' S+ L
HAL_StatusTypeDef HAL_ADC_PollForConversion(ADC_HandleTypeDef *hadc, uint32_t Timeout)
功能:等待 ADC 规则组转换完成;
参数 1:ADC 句柄,根据实际需要填写;
参数 2:超时时间,单位 ms;
返回:操作结果,HAL_OK,HAL_ERROR,HAL_TIMEOUT;
示例:HAL_ADC_PollForConversion(&hadc1, 10); //等待转换完成
1 }- y' D; t: ^9 f" X# V4 m+ H
uint32_t HAL_ADC_GetValue(const ADC_HandleTypeDef *hadc)
功能:读取 ADC 规则组转换结果;
参数 1:ADC 句柄,根据实际需要填写;
返回:转换结果,ADC 采样寄存器值;
示例:ADC_Value = HAL_ADC_GetValue(&hadc1);// 读取转换的 AD 值
+ w5 L6 j0 O. a
核心代码:
HAL_ADCEx_Calibration_Start(&hadc1,ADC_SINGLE_ENDED); //矫正 ADC
while (1)
{
HAL_ADC_Start(&hadc1); //开启 ADC1 转换
HAL_ADC_PollForConversion(&hadc1, 10); //等待转换完成,第二个参数表示超时时间,单位ms
if(HAL_IS_BIT_SET(HAL_ADC_GetState(&hadc1), HAL_ADC_STATE_REG_EOC))
ADC_Value = HAL_ADC_GetValue(&hadc1);// 读取转换的 AD 值
ADC_Vol = ADC_Value*3.3/4096;// 转换为电压
printf("ADC_Vol: %2.4f\r\n", ADC_Vol); //通过串口发送
HAL_Delay(50);
}

& `- D5 [, l  S& o& w
以上为 main 函数中外设初始化结束后的部分,首先对 ADC 进行校准,然后进入主循环,循环中开启 ADC 转换,等待转换完成后读取转换结果,然后将结果转换为浮点数电压值,最后通过串口打印至 PC,每 50ms 进行一次测量。

9 N" P8 D% z. N! j9 ]* Z/ l% P
实验现象:
下载烧录后可以观察到上位机串口助手打印 ADC 测量数据。

  k7 p  B+ a2 b
微信图片_20241122152553.png ' E7 U+ j* U% z) o8 Z& b

* S. w, l# D# v9 o: g
▲ 实验现象

4 R* P2 A: S9 j* k

6 ~6 V0 u1 C" E3 u  ]
2、ADC 中断方式单路采集实验
/ O; B. ]3 ?5 g6 @* Z
CubeMX 配置如下,保存后生成对应的配置代码:
$ ^/ E( G$ k& ?4 a2 _
微信图片_20241122152556.png
▲ CubeMX 进行中断配置

, t6 c* ?' b( @6 d% L8 m
CubeMX 中的 ADC 基本配置与上例相同,这里需要开启 ADC1 的中断。

+ r2 L9 s; N* e- b: d
相关操作函数说明:
HAL_StatusTypeDef HAL_ADC_Start_IT(ADC_HandleTypeDef *hadc)
功能:使能 ADC,以中断开启 ADC 规则组转换;
参数 1:ADC 句柄,根据实际需要填写;
返回:操作结果,HAL_OK,HAL_ERROR;
示例:HAL_ADC_Start_IT(&hadc1); //开启 ADC1 转换
注意:在 ADC 转换完成之后会触发中断,中断中读取采样数据

1 W" F: Z$ S% O/ u  @0 E5 V
HAL_StatusTypeDef HAL_ADC_Stop_IT(ADC_HandleTypeDef *hadc)
功能:关闭 ADC,停止规则组转换,关闭转换结束中断;
参数 1:ADC 句柄,根据实际需要填写;
返回:操作结果,HAL_OK,HAL_ERRORT;

  Y/ m1 [% }' A6 m1 v
核心代码:
HAL_ADCEx_Calibration_Start(&hadc1,ADC_SINGLE_ENDED); //矫正
while (1)
{
HAL_ADC_Start_IT(&hadc1); //中断方式启动 ADC
HAL_Delay(50);
}
( X0 c8 O, @+ A" M, _3 ~
以上为 main 函数中外设初始化结束后的部分,首先对 ADC 进行校准,然后进入主循环,循环中中断模式开启 ADC 转换,每 50ms 进行一次测量。
) n* }1 d* b3 ]' O
void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef *hadc)
{
if(hadc == &hadc1){if(HAL_IS_BIT_SET(HAL_ADC_GetState(&hadc1),
HAL_ADC_STATE_REG_EOC))
ADC_Value = HAL_ADC_GetValue(&hadc1);// 读取转换的 AD 值
ADC_Vol = ADC_Value*3.3/4096;// 转换为电压
printf("ADC_Vol: %2.4f\r\n", ADC_Vol); //通过串口发送
}
}

5 U8 i9 B+ a) c3 i
以上为 ADC 转换完成中断回调函数,该函数为 ADC 共用的,进入此函数首先要判断是哪个 ADC 转换完成了,然后读取相应 ADC 的数据寄存器,转换为浮点数电压,通过串口发送到上位机。

( ^2 s. M3 d/ K7 I
实验现象:
下载烧录后可以观察到上位机串口助手打印 ADC 测量数据。

2 a# S- ?5 B" Z9 m; s- o) E
微信图片_20241122152600.png
. J* _9 J( n- _* S5 f
7 @0 U' [9 |9 y# m, q# U) ?) h6 T2 X7 |
▲ 实验现象

% }5 ^' N+ f, x( f; [$ i1 b$ l) C
3、ADC 使用 DMA 方式单路采集实验
CubeMX 配置如下,保存后生成对应的配置代码:
) y' x. x- K, h& M! d
微信图片_20241122152607.png
▲ CubeMX 进行 ADC 配置

5 D% _2 B4 I5 a" l$ T
微信图片_20241122152610.png
6 e, n  Z: ^6 C$ Z
▲ CubeMX 进行 DMA 配置
. }, h5 l+ h: u! P
CubeMX 中的 ADC 基本配置需要开启连续转换模式,使能 DMA 请求,然后需要对 ADC1的 DMA 进行配置,使用连续传输模式,半字传输。
& A8 e$ h5 m& }* F7 n% d6 ?5 |& y
相关操作函数说明:
HAL_StatusTypeDef HAL_ADC_Start_DMA(ADC_HandleTypeDef *hadc, uint32_t *pData,uint32_t Length)
功能:使能 ADC,通过 DMA 进行规则组转换;
参数 1:ADC 句柄,根据实际需要填写;
参数 2:ADC 数据读取数组指针,一般为数组首地址;
参数 3:DMA 传输长度;
返回:操作结果,HAL_OK,HAL_ERROR;
示例:HAL_ADC_Start_DMA(&hadc1,(uint32_t *)ADC_Value,ADC_BUFFER_SIZE);//开启 ADC,开始 DMA 传输;
3 g$ w( [; c+ W2 C
HAL_StatusTypeDef HAL_ADC_Stop_DMA(ADC_HandleTypeDef *hadc)
功能:关闭 ADC,停止 DMA 传输;
参数 1:ADC 句柄,根据实际需要填写;
返回:操作结果,HAL_OK,HAL_ERROR,HAL_BUSY;
示例:HAL_ADC_Stop_DMA(&hadc1);//停止 ADC

: O- ?( v5 @1 p* ^
核心代码:
if(HAL_ADCEx_Calibration_Start(&hadc1,ADC_SINGLE_ENDED) != HAL_OK) //开始
ADC 校准
{
Error_Handler();
}
if(HAL_ADC_Start_DMA(&hadc1,(uint32_t *)ADC_Value,ADC_BUFFER_SIZE) !=HAL_OK) //开始 DMA 传输
{
Error_Handler();
}
* o( W; ]; r  b0 x$ c
以上为 main 函数中外设初始化结束后的部分,首先对 ADC 进行校准,然后开启 ADC,使用 DMA 进行传输。
+ H, z+ Q. ^1 J8 E6 x" x0 D( w2 a
void ADC_DMA_Handle(void)
{
if(__HAL_DMA_GET_FLAG(&hdma_adc1,DMA_FLAG_TC1))//检查 DMA 传输完成标志
{
__HAL_DMA_CLEAR_FLAG(&hdma_adc1,DMA_FLAG_TC1);//清楚 DMA 传输完成标志
HAL_ADC_Stop_DMA(&hadc1);//停止 ADC
float ave_vol = 0;uint16_t all=0;
for(uint8_t i = 0;i<ADC_BUFFER_SIZE;i++)
{
all += ADC_Value;
}
all = all/ADC_BUFFER_SIZE;
ave_vol = 3.3f/4096*all;
printf("ave_vol is %1.2f V \r\n",ave_vol);
HAL_ADC_Start_DMA(&hadc1,(uint32_t *)ADC_Value,ADC_BUFFER_SIZE);//重启 ADC
}
}
以上为中断处理函数,需要添加到 DMA 中断中。当进入 DMA 传输完成中断之后,该函数先停止 ADC 采集,对上一轮 DMA 采集到的数据进行求均值,然后转换为相应的浮点电压发送到上位机,最后重启 ADC 转换。
" w* x3 U! B5 T* n7 R1 i7 G8 v
void DMA1_Channel1_IRQHandler(void)
{
ADC_DMA_Handle();
HAL_DMA_IRQHandler(&hdma_adc1);
}
: Z# U( i! s2 l' Z, k- K$ B+ Q
以上为 DMA 中断处理函数,在其中添加 ADC_DMA_Handle();。

0 t4 F( y% _. H$ `" j9 Y
实验现象:
下载烧录后可以观察到上位机串口助手打印 ADC 测量数据。
" }) i! N8 e% x6 c. V
微信图片_20241122152613.png 3 j. @7 T) r( p; t- o# {0 n7 X  e

( S3 V3 N( e8 r  c) ?# F
▲ 实验现象
8 B: c' D' d3 B
4、内部温度采集实验

, E% |- @( @# \
CubeMX 配置如下,保存后生成对应的配置代码:
% g' U: G  ?/ N4 B- q2 v: @
微信图片_20241122152616.png 9 x2 S" H' t2 e7 W8 J' @

1 Z' z6 P; `, Y7 @) I
▲ CubeMX 进行温度传感器 ADC 配置
  S; p, ?1 \& M8 u3 S9 N& W
本实验进行内部温度传感器读取,需要注意采样时间需要给足,手册要求最小采样时间 5us,根据时钟频率进行换算。
4 o- y' t( W" V& |% r0 j
相关操作函数说明:
__HAL_ADC_CALC_TEMPERATURE(__VREFANALOG_VOLTAGE__,__TEMPSENSOR_ADC_DATA__, __ADC_RESOLUTION__)
功能:将内部温度传感器的 ADC 采样值转换为温度;
参数 1:ADC 参考电压,单位 mv;
参数 2:ADC 采样寄存器数据,注意是读取的原始数据;
参数 3:ADC 采样位数,可选 ADC_RESOLUTION_12B、ADC_RESOLUTION_10B、ADC_RESOLUTION_8B、ADC_RESOLUTION_6B;
返回:转换后的温度值;
示例:
tem=__HAL_ADC_CALC_TEMPERATURE(vdda,ADC_Value,ADC_RESOLUTION_12B);//转换温度
( ^& U( C' J, q& p8 |
核心代码:
HAL_ADCEx_Calibration_Start(&hadc1,ADC_SINGLE_ENDED); //矫正
while (1)
{
HAL_ADC_Start(&hadc1);
HAL_ADC_PollForConversion(&hadc1, 10); //等待转换完成,第二个参数表示超时时间,单位ms
if(HAL_IS_BIT_SET(HAL_ADC_GetState(&hadc1), HAL_ADC_STATE_REG_EOC))
ADC_Value = HAL_ADC_GetValue(&hadc1);// 读取转换的 AD 值
ADC_Vol = ADC_Value*3.3/4096;// 转换为电压
float tem;
tem=__HAL_ADC_CALC_TEMPERATURE(vdda,ADC_Value,ADC_RESOLUTION_12B);//转换温度
printf("ADC_Vol: %2.4f V Tem: %2.4f ℃\r\n", ADC_Vol,tem); //通过串口发送
HAL_Delay(500);
}
! P* U: {4 z- @1 n( V
以上为 main 函数中外设初始化结束后的部分,首先对 ADC 进行校准,然后进入主循环,循环中开启 ADC 转换,等待转换完成后读取转换结果,然后将调用自带的温度转换函数将 ADC 采样值转换为温度,最后通过串口打印至 PC,每 500ms 进行一次测量。

' E3 F" D9 w, q
实验现象:
下载烧录后可以观察到上位机串口助手打印温度测量数据。

, V% U3 L3 N  G& c' X6 d+ r& ]# e
微信图片_20241122152619.png ; G. G( e: E) F* @# }8 U
▲ 实验现象

0 o3 k0 G6 ~" _+ P8 `  l, K1 [
5、VABT 电压采集实验
CubeMX 配置如下,保存后生成对应的配置代码:
; V1 T# N- ?) \; H# W
微信图片_20241122152621.png
7 z1 l, t( ~" r4 t8 x

: Q( R# ]- ~' \' K
▲ CubeMX 进行 ADC 配置
2 e# L! z; @( O1 T2 C
本实验进行 VBAT 电压读取,基本配置与例 3.8.1 相同,需要注意采样时间需要给足,手册要求最小采样时间 12us,根据时钟频率进行换算。
! b, h) T! A9 m
核心代码:
HAL_ADCEx_Calibration_Start(&hadc1,ADC_SINGLE_ENDED); //矫正
while (1)
{
HAL_ADC_Start(&hadc1);
HAL_ADC_PollForConversion(&hadc1, 10); //等待转换完成,第二个参数表示超时时间,单位 ms
if(HAL_IS_BIT_SET(HAL_ADC_GetState(&hadc1), HAL_ADC_STATE_REG_EOC))
ADC_Value = HAL_ADC_GetValue(&hadc1);// 读取转换的 AD 值
ADC_Vol = 3*ADC_Value*3.3f/4096;// 转换为电压
printf("VBAT: %2.4f V \r\n", ADC_Vol); //通过串口发送
HAL_Delay(500);
}

: H3 [9 ?6 t/ x# {
以上为 main 函数中外设初始化结束后的部分,首先对 ADC 进行校准,然后进入主循环,循环中开启 ADC 转换,等待转换完成后读取转换结果,然后将结果转换为浮点数电压值,需要注意的是,VBAT 采样在内部进行了 1/3 分压,因此最终电压计算结果需要乘 3,最后通过串口打印至 PC,每 500ms 进行一次测量。
" ^2 g* i' z$ \: w; N6 `
实验现象:
下载烧录后可以观察到上位机串口助手打印 ADC 测量数据。
6 Q( m8 Z7 R+ V
微信图片_20241122152624.png
▲ 实验现象

( n5 ]1 Z  b4 @: l+ E
6、内部基准电压采集实验
CubeMX 配置如下,保存后生成对应的配置代码:
微信图片_20241122152627.png
/ i+ x' h8 X& `1 Z. s/ |

' A7 a8 T+ F* F/ F# N4 \7 ]
▲ CubeMX 进行温度传感器 ADC 配置

# H; g* C6 T# c* [0 R$ h8 K
本实验进行内部参考电压读取,基本配置与例 3.8.1 相同,需要注意采样时间需要给足,手册要求最小采样时间 4us,根据时钟频率进行换算,读取之后通过内部参考电压反算外部参考电压。
- I* L, \; J% y. d% Q
相关操作函数说明:
__HAL_ADC_CALC_VREFANALOG_VOLTAGE(__VREFINT_ADC_DATA__,__ADC_RESOLUTION__)
功能:通过读取到的内部参考电压,反算实际参考电压;
参数 1:ADC 采样寄存器数据,注意是读取的原始数据;
参数 2:ADC 采样位数,可选 ADC_RESOLUTION_12B、ADC_RESOLUTION_10B、ADC_RESOLUTION_8B、ADC_RESOLUTION_6B;
返回:转换后的时间参考电压,单位 mv;
示例:VREF_MV =__HAL_ADC_CALC_VREFANALOG_VOLTAGE(ADC_Value,ADC_RESOLUTION_12B);//转换 VREF+
# n$ g( ^) |/ }( c& |  z# T
核心代码:
HAL_ADCEx_Calibration_Start(&hadc1,ADC_SINGLE_ENDED); //矫正
while (1)
{
HAL_ADC_Start(&hadc1);
HAL_ADC_PollForConversion(&hadc1, 10); //等待转换完成,第二个参数表示超时时间,单位 ms
if(HAL_IS_BIT_SET(HAL_ADC_GetState(&hadc1), HAL_ADC_STATE_REG_EOC))
ADC_Value = HAL_ADC_GetValue(&hadc1);// 读取转换的 AD 值
VREF_MV =
__HAL_ADC_CALC_VREFANALOG_VOLTAGE(ADC_Value,ADC_RESOLUTION_12B);//转换 VREF+
printf("VREF+: %d mV \r\n", VREF_MV); //通过串口发送
HAL_Delay(500);
}

8 h( [/ B5 M) z+ ]/ D
以上为 main 函数中外设初始化结束后的部分,首先对 ADC 进行校准,然后进入主循环,循环中开启 ADC 转换,等待转换完成后读取转换结果,然后将调用自带的电压转换函数将内部参考电压 ADC 采样值转换为实际外部参考电压输入,最后通过串口打印至 PC,每500ms 进行一次测量。
* {' l# `# ]0 Y( w( G  u
实验现象:
下载烧录后可以观察到上位机串口助手打印外部参考电压测量数据。

; J  o8 z7 N( N+ Y% C0 a
微信图片_20241122152630.png ; _+ ]9 @7 w8 r" S  u

1 r6 F3 e* z$ v+ |+ S4 t6 ?3 w
▲ 实验现象

( J6 F7 e) Z" W9 |
7、定时器触发单通道 ADC 采样
CubeMX 配置如下,保存后生成对应的配置代码:
微信图片_20241122152633.png
% \5 `! e& C9 E% a5 G2 a
) c. ]4 K$ f1 ^: Q6 I
▲ CubeMX 进行 ADC 触发配置
微信图片_20241122152635.png ! ?( h  F/ @! y* T$ o: S" c

  e& ^) }) G7 b( Z
▲ CubeMX 进行定时器配置
微信图片_20241122152638.png # q2 u% w- C; e9 p

+ X, G  L2 \, t- t
▲ CubeMX 进行中断配置
3 _% [( @! ?, l
CubeMX 中的 ADC 基本配置单通道采样相同,这里需要开启 ADC1 的中断,并且修改转换触发源,原来的软件触发改为使用定时器时间进行触发,TIM1 配置周期为 10ms,即每 10ms触发一次 ADC 转换。
4 F9 G3 z  G2 ^4 ^5 H2 W, K( H
相关操作函数说明:
HAL_StatusTypeDef HAL_ADC_Start_IT(ADC_HandleTypeDef *hadc)
功能:使能 ADC,以中断开启 ADC 规则组转换;
参数 1:ADC 句柄,根据实际需要填写;
返回:操作结果,HAL_OK,HAL_ERROR;
示例:HAL_ADC_Start_IT(&hadc1); //开启 ADC1 转换
注意:在 ADC 转换完成之后会触发中断,中断中读取采样数据

4 b" G) ~$ a& i: M
HAL_StatusTypeDef HAL_ADC_Stop_IT(ADC_HandleTypeDef *hadc)
功能:关闭 ADC,停止规则组转换,关闭转换结束中断;
参数 1:ADC 句柄,根据实际需要填写;
返回:操作结果,HAL_OK,HAL_ERRORT;

/ H% }4 W5 [' K
核心代码:
HAL_ADCEx_Calibration_Start(&hadc1,ADC_SINGLE_ENDED); //矫正
HAL_ADC_Start_IT(&hadc1);//中断方式启动 ADC
HAL_TIM_Base_Start(&htim1);//启动 TIM1

- V/ d( j! ?, ]6 V
以上为 main 函数中外设初始化结束后的部分,首先对 ADC 进行校准,然后中断方式开启ADC 转换,这里主要是要开启 ADC 并且使能中断,然后开启 TIM1,通过 TIM 触发 ADC进行转换。
2 i- S4 w, G/ S" y, o! N# Y
void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef *hadc)
{
if(hadc == &hadc1)
{
if(HAL_IS_BIT_SET(HAL_ADC_GetState(&hadc1),
HAL_ADC_STATE_REG_EOC))
ADC_Value = HAL_ADC_GetValue(&hadc1);// 读取转换的 AD 值
ADC_Vol = ADC_Value*3.3/4096;// 转换为电压
printf("ADC_Vol: %2.4f\r\n", ADC_Vol); //通过串口发送
}
}

- g' a, W( p5 _$ o* o. [# d/ k# x
以上为 ADC 转换完成中断回调函数,该函数为 ADC 共用的,进入此函数首先要判断是哪个 ADC 转换完成了,然后读取相应 ADC 的数据寄存器,转换为浮点数电压,通过串口发送到上位机。

/ D& v8 Z# R4 G5 A8 @& T
实验现象:
下载烧录后可以观察到上位机串口助手打印 ADC 测量数据。
6.png 8 y* C/ B* G, F1 B
6 l7 |" S8 t3 P
▲ 实验现象
九、DAC实验
# \- w, g/ x( ]' z' q
实验目的:掌握和熟悉 DAC 单路输出的软件触发和定时器触发配置方法,配合 DMA 输出波形。
3 ^$ f" }2 i4 u- J' M! w) G
1、DAC 软件触发输出实验

, w5 L4 U- j: k  z+ X+ c
CubeMX 配置如下,保存后生成对应的配置代码:
) i  c: G$ t* \" ?! _. n6 ]
微信图片_20241122152645.png
5 v7 A7 ^& W6 v7 L$ f4 Y# r

* [$ f4 _+ Y2 ]/ [$ T9 l+ k1 v- X
▲ CubeMX 进行 DAC 输出配置
( d. }9 p- ~& R* L) V2 c
本实验进行软件触发 DAC 输出,开启 DAC1 的 OUT1 输出,使用外部输出引脚,使用普通模式,并且使能输出缓冲,将触发设置为软件触发。

/ ]& A' Q; q( ~! C
相关操作函数说明:
HAL_StatusTypeDef HAL_DAC_SetValue(DAC_HandleTypeDef *hdac, uint32_t Channel,uint32_t Alignment, uint32_t Data)
功能:设置 DAC 输出电压;
参数 1:DAC 句柄,根据需要填写;
参数 2:DAC 通道,可选 DAC_CHANNEL_1、DAC_CHANNEL_2;
参数 3:DAC 数据格式,可选 DAC_ALIGN_12B_R(12 位右对齐)、DAC_ALIGN_12B_L(12 位左对齐)、DAC_ALIGN_8B_R(8 位右对齐);
参数 4:要写入的电压数据;
返回:操作结果,HAL_OK 或 HAL_ERROR;
示例:HAL_DAC_SetValue(&hdac1, DAC_CHANNEL_1, DAC_ALIGN_12B_R,sinewave[temp_i]);// 设置输出值注意:此函数不会改变实际的 DAC 输出,如果想要修改生效,还需要使用下面的函数
8 }' c+ \2 {( \8 w
HAL_StatusTypeDef HAL_DAC_Start(DAC_HandleTypeDef *hdac, uint32_t Channel)
功能:开启外部 DAC 电压转换;
参数 1:DAC 句柄,根据需要填写;
参数 2:DAC 通道,可选 DAC_CHANNEL_1、DAC_CHANNEL_2;
返回:操作结果,HAL_OK 或 HAL_ERROR;
示例:HAL_DAC_Start(&hdac1,DAC_CHANNEL_1);// 改变输出值
( c5 v3 K7 S! x) y: ]# |, X/ K2 x

  x+ W; I# S) J
HAL_StatusTypeDef HAL_DAC_Stop(DAC_HandleTypeDef *hdac, uint32_t Channel)
功能:停止外部 DAC 电压转换;
参数 1:DAC 句柄,根据需要填写;
参数 2:DAC 通道,可选 DAC_CHANNEL_1、DAC_CHANNEL_2;
返回:操作结果,HAL_OK 或 HAL_ERROR;

% a" H# ]8 ~1 A9 T" d( a9 g% N/ e6 T
核心代码:
while (1)
{
for(temp_i=0; temp_i<60; temp_i++)
{
HAL_DAC_SetValue(&hdac1, DAC_CHANNEL_1, DAC_ALIGN_12B_R,
sinewave[temp_i]);// 设置输出值
HAL_DAC_Start(&hdac1,DAC_CHANNEL_1);// 改变输出值
HAL_Delay(1);// 延时一毫秒
}
}

% o/ \* p8 Y- ?, q  {6 G
以上为 main 函数中外设初始化结束后的部分,主循环中根据正弦表切换 DAC 电压输出,1ms 进行一次切换,正选表一共 60 个点。
8 @! C3 E6 `. K4 L/ l
实验现象:下载烧录后可以观察到 PA4 输出一个正弦波,频率约为 8.333Hz。

9 J* W! j. n3 Y# b- d+ c1 a  u" e
微信图片_20241122165909.png
5 Q* C# y3 m3 }) B9 i! k6 E
0 Y5 r/ P$ \) i) W4 Z3 f1 \5 R
▲ 实验现象
$ H9 D4 _+ a9 z0 U* t0 N
2、定时器触发 DMA 传输 DAC 输出实验

6 s' R; `5 V8 K8 \# H
CubeMX 配置如下,保存后生成对应的配置代码:
微信图片_20241122152650.png 3 e- M! x! L" R! ?$ b

# b/ ~+ x1 O5 c* p/ L/ t7 S
▲ CubeMX 进行 DAC 基本配置
微信图片_20241122152652.png
4 o, Y' \* Q+ j, E2 h: A
8 v4 Y9 h# S3 V- m" @8 f+ y& e' A
▲ CubeMX 进行 DMA 配置
微信图片_20241122152654.png
  v& e; _. J; j9 u, W" x

- r0 g+ B7 U' J7 C! Y% d! l$ L1 K& W
▲ CubeMX 进行 TIM4 配置
! P0 h7 D) s! I5 N2 e' D
本实验进行定时器触发 DAC 输出,开启 DAC1 的 OUT1 输出,使用外部输出引脚,使用普通模式,并且使能输出缓冲,将触发设置为 TIM4 触发,配置 DMA,使用循环模式,整字传输,配置 TIM4,设置定时器周期为 1ms。
5 i+ z( ^6 R5 `6 M$ {8 x
相关操作函数说明:
HAL_StatusTypeDef HAL_DAC_Start_DMA(DAC_HandleTypeDef *hdac, uint32_t Channel,const uint32_t *pData, uint32_t Length,uint32_t Alignment)
功能:通过 DMA 方式开始 DAC 转换;
参数 1:DAC 句柄,根据需要填写;
参数 2:DAC 通道,可选 DAC_CHANNEL_1、DAC_CHANNEL_2;
参数 3:要通过 DMA 发送的数据指针,一般为数据首地址;
参数 4:要通过 DMA 发送的数据长度;参数 5:发送数据格式,可选 DAC_ALIGN_12B_R(12 位右对齐)、DAC_ALIGN_12B_L(12 位左对齐)、DAC_ALIGN_8B_R(8 位右对齐);
返回:操作结果,HAL_OK 或 HAL_ERROR;
示例:HAL_DAC_Start_DMA(&hdac1, DAC_CHANNEL_1,(uint32_t *)dac_wave1,SAWTOOTH_NB_STEPS,DAC_ALIGN_12B_R) ;// DMA 方式设置输出值
4 @* _, i7 d) f2 _7 {* H. w
核心代码:
//正弦表
uint32_t dac_wave1[80]={
0x0826,0x08C6,0x0965,0x0A02,0x0A9C,0x0B31,0x0BC2,0x0C4C,0x0CD0,0x0D4C,0x0DC0,0x0E2B,0x0E8C,0x0EE2,0x0F2E,0x0F6E,0x0FA3,0x0FCC,0x0FE8,0x0FF8,0x0FFB,0x0FF1,0x0FDB,0x0FB9,0x0F8A,0x0F50,0x0F0A,0x0EB8,0x0E5D,0x0DF7,0x0D87,0x0D0F,0x0C8F,0x0C08,0x0B7A,0x0AE7,0x0A4F,0x09B4,0x0916,0x0876,0x07D5,0x0735,0x0696,0x05F9,0x055F,0x04CA,0x0439,0x03AF,0x032B,0x02AF,0x023B,0x01D0,0x016F,0x0119,0x00CD,0x008D,0x0058,0x002F,0x0013,0x0003,0x0000,0x000A,0x0020,0x0042,0x0071,0x00AB,0x00F1,0x0143,0x019E,0x0204,0x0274,0x02EC,0x036C,0x03F3,0x0481,0x0514,0x05AC,0x0647,0x06E5,0x0785};
//正弦表点数
#define SAWTOOTH_NB_STEPS 80

! ^- L9 R) D3 h& F
以上为正弦表定义。

, M- z# M0 V0 X0 T/ H
if (HAL_TIM_Base_Start(&htim4) != HAL_OK)//开启定时器 4
{
Error_Handler();
}
if (HAL_DAC_Start_DMA(&hdac1, DAC_CHANNEL_1,(uint32_t
*)dac_wave1,SAWTOOTH_NB_STEPS,DAC_ALIGN_12B_R) != HAL_OK) //开始 DMA 传输
{
Error_Handler();
}

  g0 o; B; `' G1 U5 w
以上为 main 函数中外设初始化结束后的部分,开启 TIM4 进行触发,以 DMA 方式开启DAC 转换输出。

5 K; P, N( u# n" p2 _" O
实验现象:
下载烧录后可以观察到 PA4 输出一个正弦波,频率约为 12.5Hz。
微信图片_20241122152657.png
" e8 x2 ~" f9 C

5 f3 E, w" C  Y' r6 B/ q& L- P
▲ 实验现象

; s) s" ^5 q& X
3、定时器触发 DAC 输出噪声实验
" _! Z4 h2 |: ^5 r9 F
CubeMX 配置如下,保存后生成对应的配置代码:
微信图片_20241122152659.png ; ]8 ~  j: C1 }8 y6 n
▲ CubeMX 进行 DAC 输出配置
微信图片_20241122152701.png
$ u  J& a5 H& D3 N) Q7 S3 `

$ Z" S# Z, Q! g$ h& C
▲ CubeMX 进行 TIM2 配置

6 F1 ~2 v2 T% G3 R  W* O3 [
本实验使用 TIM2 触发 DAC 进行输出,输出内容由 DAC 随机生成,产生噪声。

* j3 ~/ t+ [9 g4 Q
核心代码:
HAL_DAC_Start(&hdac1,DAC_CHANNEL_1);//启动 DAC 输出
HAL_TIM_Base_Start(&htim2);//启动 TIM2 触发 DAC

* R3 d) L0 I$ M, Q
以上为 main 函数中外设初始化结束后的部分,只需要开启 DAC 输出和定时器即可。

( z7 d3 u- C/ ]* T' N2 s3 v
实验现象:
下载烧录后可以观察到 PA4 输出随机噪声。
微信图片_20241122152703.png
7 d) F- b* E8 v
▲ 实验现象
$ q! \6 u  l. X4 z) Q5 l& q
如有侵权请联系删除
) q7 G- q% l" x8 ~$ I, u! c
转载自:AI电堂
! _# f5 H" @. N, x

% j8 ^- G9 F  A+ U4 ]
收藏 评论0 发布时间:2024-11-22 15:27

举报

0个回答

所属标签

相似分享

官网相关资源

关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
STM32Cube扩展软件包
意法半导体边缘AI套件
ST - 理想汽车豪华SUV案例
ST意法半导体智能家居案例
STM32 ARM Cortex 32位微控制器
关注我们
st-img 微信公众号
st-img 手机版