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

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

[复制链接]
攻城狮Melo 发布时间:2024-11-22 15:27
六、SPI 实验
# `8 j) [! @' y) J( O6 ^实验目的:掌握和熟悉 SPI 软件模拟和硬件控制的使用和配置方法。* U$ N% m5 U( `/ T

+ W: K8 ]. _- l9 N& S1、软件模拟 SPI 驱动 TFT 实验, j+ y1 @# H. m5 D! I
CubeMX 配置如下,保存后生成对应的配置代码:6 {$ S; \4 a( l2 e, ?6 O' @) P9 i) W
13.png
# Z) ?  e  M2 `. ?9 V

( O- E" `$ z# g8 l8 ~
▲ CubeMX 配置

9 c6 ^) A) Y. D5 A8 V3 W
本实验使用软件模拟 SPI,只需要对相应 IO 进行配置即可,注意需要配置 IO 速度等级,CLK 信号和 SDA 信号频率较高,需要配置为 very high。1 I$ Y" ^0 P' P, M

' F, X' j9 o+ F/ _, [2 y! w2 o0 \
相关操作函数说明:
8 P8 Z8 f+ J5 nvoid Lcd_Reset(void)" o7 m' i9 g- X0 n6 B+ ?% A
1 n9 _' W0 T) N! i; v
功能:液晶硬复位函数;
' |- g; o5 l  f- M参数:无;) h% Y+ j# X4 m6 y- r. C7 H
返回:无;
% z: T1 @) t" T' _, c说明:液晶初始化前需执行一次复位操作
9 u' K: S3 Y3 S% Z9 x7 t% Qvoid LCD_Initial(void)
/ d* E9 b/ q% H功能:初始化液晶;
4 G3 W- O" L) N5 k% D参数:无;
! C5 O* Q2 U1 I6 W. }, [返回:无;9 p& n& W! @1 G! X* q- w
说明:在对液晶写入内容前需要进行初始化配置;
) C% T) \! r9 r5 i) p1 m- @( H0 b3 L2 rvoid Lcd_ColorBox(unsigned int xStart,unsigned int yStart,unsigned int xLong,unsigned int yLong,unsigned int Color), `2 J. s# N# z5 P2 O

0 N! v; n+ R: I* I( }功能:Lcd 矩形填充函数;/ }. Q9 b# {# f/ h+ X
参数 1:x 方向的起始点;. }2 h' R( C, m* W- j' U: B
参数 2:y 方向的起始点;
6 ?1 v" b$ R( N3 K2 c& F9 [- a参数 3:x 方向的长度;
6 p3 X2 P) s7 e) y4 u参数 4:y 方向的长度;
( R& ^  t! x1 u9 V) o: C参数 5:填充的颜色;% _, I* c5 F9 F2 f/ J* }
返回:无;% Q+ V2 v9 k5 a% o- Z; M( n1 u5 T
说明:将指定区域内填充指定颜色,常用于清屏/ L, o) @- Q+ [6 {1 H1 Y# T
void BlockWrite(unsigned int Xstart,unsigned int Xend,unsigned int Ystart,unsigned int Yend)
, o4 X  D5 i$ A! H0 R' X! [6 J3 r8 N7 I/ ^
功能:在一个指定位置开一个矩形框;+ S, u7 T7 D& [$ a) p& _
参数 1:x 方向的起始点;
; ~4 S; Z+ z6 z8 f! |% u参数 2:x 方向的终点;% E- B  y5 @, e
参数 3:y 方向的起始点;
# i' `$ M2 @% m: K参数 4:y 方向的终点;4 }9 N: s% H8 B% x) A( w( G0 x: a
返回:无;5 y0 {! q8 \4 P- k
说明:开一个矩形框,方便接下来往这个框填充数据;% r. u5 K6 W# M( x; q* w0 P
void DrawPixel(unsigned int x, unsigned int y, int Color)
" V. W0 [. ], F功能:在 x,y 坐标上打一个颜色为 Color 的点;
2 a/ Z9 _1 S" H7 K9 W; H  ^. D" C参数 1:x 坐标;
; |7 k2 G# }" }4 h; A参数 2:y 坐标;
0 U3 i4 x4 L& O0 q7 o, F参数 3:点的颜色;& @) q% _4 e" d" N  @" X8 w4 _
返回:无;( G' N+ o; i! J) O3 v
void LCD_PutString(unsigned short x, unsigned short y, char *s, unsigned int fColor, unsigned int bColor,unsigned char flag)/ U, l8 T+ u- N0 K
- \4 \6 Z! F5 w9 S! L6 `" J- B; @
功能:显示一个字符串;
4 l5 n/ d+ g+ v1 C8 i* d9 n参数 1:起始点 x 坐标;0 n  c' M/ W7 ~& @: r& m
参数 2:起始点 y 坐标;: ~" A$ b6 [8 K9 U" n1 Q/ z
参数 3:字符串指针;
0 i( u9 r7 B2 J参数 4:前景色;
! r# C* e6 ~8 g参数 5:背景色;1 A& |- V$ @; O2 Q1 J$ C& x' Y
参数 6:有无背景色;' G3 i4 G* {7 \& ?* E/ u" G
返回:无* B3 a  m" x0 D% F1 I

  l6 y2 Y! S( x( w7 ]" W" X9 @

2 L4 Z9 v( R. H$ J+ u1 b* L! X" B核心代码:
7 Z" N! g8 S' t( ~" o& CLCD_Initial();
1 _: i& G* t2 o7 @Lcd_ColorBox(0,0,XSIZE_PHYS,YSIZE_PHYS,Blue);//用蓝色清屏: F8 X/ [9 e! u# h5 @
Lcd_ColorBox(0,0,XSIZE_PHYS,YSIZE_PHYS,Red);//用红色清屏
4 ]/ r8 i, @/ T8 r5 s$ E  y2 S' G+ nLcd_ColorBox(0,0,XSIZE_PHYS,YSIZE_PHYS,Green);//用绿色清屏. Q# e, K6 A3 }
Lcd_ColorBox(0,0,XSIZE_PHYS,YSIZE_PHYS,White);//用白色清屏6 a0 P1 S" m6 _; b1 A: B
LCD_PutString(10,10,"STM32G474Test",Red,White,0);//显示字符) _; s) e  ~' w% H3 u

( ]: P, [) D8 a9 u在 main 函数中进行过外设初始化之后,对 LCD 进行初始化,然后分别用四种颜色清屏,最后显示测试字符。
( y; I: e7 ?! _+ b" X
/ F# ^0 A) G9 E9 e# {

6 }( @; n5 c* `. j2 J- K" }实验现象:
1 b0 |% D& N0 ?- p4 T, }2 Q下载烧录后可以观察到屏幕分别刷新蓝红绿白四种颜色,最后显示测试字符STM32G474Test。; @( R8 |$ _8 d7 w% T1 A4 i' y

4 {! F1 j$ o7 |) @0 Y: S
7 w4 p$ V0 I! L% j0 f/ u) r
2、硬件 SPI 驱动 TFT 实验. W! K# W- Q* U
CubeMX 配置如下,保存后生成对应的配置代码:7 n$ @1 O0 q0 s# v2 y) M: W2 |
& `- f  O; A- W3 r8 v# C* D
12.png
$ l5 S: o. O# h. N
4 l8 H- ?# e. x
▲ CubeMX 进行 SPI 配置
微信图片_20241122152531.png
/ J. R' F( H  W& [. i2 G
1 d  {/ l1 b2 I- d: `& l$ z' }
▲ CubeMX 进行 IO 速度配置  a: j8 ?5 B* Y  C, H4 k
本实验使用硬件 SPI,需要配置 SPI 的时钟分频,配置出合适的时钟速率,另外需要注意设置时钟信号的空闲电平以及采样边沿,还需要将高速的信号 IO 速度进行配置,其他 IO配置与软件模拟 SPI 相同。

; w' r4 t  A; L% n/ {& I

# x0 v( e5 J9 U# ^9 p, m) e
& `% ^4 u! N8 k* ^( P9 q: D

0 M; ^: {! Z$ b$ F/ [& o
相关操作函数说明:
& C0 S1 d  j' f8 ?
HAL_StatusTypeDef HAL_SPI_Transmit(SPI_HandleTypeDef *hspi, uint8_t *pData, uint16_t Size, uint32_t Timeout)

1 V% C- b; ]( ~; G( J
功能:通过硬件 SPI 发送一组数据;
  l" g" s! p9 B2 G/ z2 g: S
参数 1:SPI 句柄,根据实际需要填写;
0 [) R( q9 H- b0 J
参数 2:要发送数据的指针,常见为发送数据数组的首地址;
* L+ n( S  ^9 r9 y
参数 3:发送数据长度,单位字节;

  n. l' E3 j0 @/ @
参数 4:发送超时时间,单位 ms;
* g3 L0 W* r7 d0 H, n
返回:操作结果,HAL_OK,HAL_ERROR;

, a6 I' `9 r1 s7 }. e4 A6 u
示例:HAL_SPI_Transmit ( &hspi4,data_color,2*xLong,10 );//通过 SPI4 发送颜色数据

" [# W* l- O0 H- B5 \  `% O
HAL_StatusTypeDef HAL_SPI_Receive(SPI_HandleTypeDef *hspi, uint8_t *pData, uint16_t Size, uint32_t Timeout)
3 A7 }& J0 Q* {7 e* G5 K  i! W
功能:通过硬件 SPI 接收一组数据;
+ Q, i& Y  c4 p. N2 D
参数 1:SPI 句柄,根据实际需要填写;

& U' g$ f2 S; ^; H) H; w9 q& j
参数 2:要接收数据保存指针;

3 s. S6 F  q8 A3 [* ^! ~3 c) O
参数 3:接收数据长度,单位字节;
7 ]" c0 }' f: c7 G
参数 4:接收超时时间,单位 ms;

7 R1 l" d7 M$ [1 E
返回:操作结果,HAL_OK,HAL_ERROR;

! \2 C0 w- P: z5 ~
HAL_StatusTypeDef HAL_SPI_TransmitReceive(SPI_HandleTypeDef *hspi, uint8_t *pTxData, uint8_t *pRxData, uint16_t Size, uint32_t Timeout)

9 P0 }, U- r4 z4 _' ^
功能:通过硬件 SPI 交换一组数据;

" E: f0 f- j- ^8 ]: c
参数 1:SPI 句柄,根据实际需要填写;

! y0 z0 N7 @( s/ @7 c, b
参数 2:要发送数据的指针,常见为发送数据数组的首地址;

2 C, f7 Z+ u$ v; b3 q6 b' y% Z4 i
参数 3:要接收数据的指针,接收数据数组的首地址;
7 ^/ C9 y2 B. [, I
参数 4:数据长度,单位字节;

- K6 @6 q* E2 e7 A
参数 5:超时时间,单位 ms;

" B3 x2 x" P& f, S0 w
返回:操作结果,HAL_OK,HAL_ERROR;

0 h! z- X9 L5 l8 v% A

" `8 ]! R- ?3 A: E
* m+ \" k  |7 g, E& H
核心代码:
6 j1 m2 @# v2 @% P; g
LCD_Initial();
) k& o) Y' ~' p# Y, o5 g
Lcd_ColorBox(0,0,XSIZE_PHYS,YSIZE_PHYS,Blue);//用蓝色清屏

- P- @) q: ~: R# X8 b2 }, V
Lcd_ColorBox(0,0,XSIZE_PHYS,YSIZE_PHYS,Red);//用红色清屏
  r) Y. z. B. t4 A/ Y6 L
Lcd_ColorBox(0,0,XSIZE_PHYS,YSIZE_PHYS,Green);//用绿色清屏

) a0 l0 r/ a, `* ], P
Lcd_ColorBox(0,0,XSIZE_PHYS,YSIZE_PHYS,White);//用白色清屏

4 A4 m# X, g0 c' W9 Z& Y- V1 }' x
LCD_PutString(10,10,"STM32G474Test",Red,White,0);//显示字符

+ L! o9 p2 B4 e6 e& e% b

9 l- K# O8 M5 V1 x2 M9 s2 ~

  V# U3 \- ], k
在 main 函数中进行过外设初始化之后,对 LCD 进行初始化,然后分别用四种颜色清屏,最后显示测试字符。
% r: Y( L7 `# n' B( ~9 ?# p0 I

. W  g( I( r7 [' v- Q, D$ \+ w) ~

3 S/ j" v' _& m4 V/ O% E
实验现象:
! K% Y% T1 s- i4 q
下载烧录后可以观察到屏幕分别刷新蓝红绿白四种颜色,最后显示测试字符STM32G474Test。

2 [2 b% ~7 Z: X* A* U8 r

5 L& j7 Q* A2 \4 `

3 D! S/ S% V2 W4 a; E* c4 }
3、硬件 SPI 驱动 TFT 实验(DMA)
/ M0 r! S$ ?3 J% g! U( [9 }
CubeMX 配置如下,保存后生成对应的配置代码:
微信图片_20241122152534.png

( E  V3 w2 k9 j7 U" S/ K; a
▲ CubeMX 进行 SPI 的 DMA 配置
本实验使用硬件 SPI,使用 DMA 进行发送。

2 d5 v6 F1 o0 S4 V# n7 K6 d
相关操作函数说明:
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)

* n( d3 M* l% L) e- f5 t
功能:通过硬件 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)
/ Y% G1 |( N4 c8 c, o. U
功能:通过硬件 SPI 的 DMA 方式交换一组数据;
参数 1:SPI 句柄,根据实际需要填写;
参数 2:要发送数据的指针,常见为发送数据数组的首地址;
参数 3:要接收数据的指针,接收数据数组的首地址;
参数 4:数据长度,单位字节;
返回:操作结果,HAL_OK,HAL_ERROR;
注意:使用相应 DMA 时需要对该 DMA 请求进行配置;

: A% a9 T+ r" N* W6 X, D5 D- C# W
核心代码:
//发送函数修改
if((temp+1) % xLong == 0)
{
HAL_SPI_Transmit_DMA(&hspi4,data_color,2*xLong);
while(!dma_flag_temp);
dma_flag_temp = 0;
}

. `3 \' f1 J! R9 ?2 e. C$ @
使用 DMA 方式进行发送时需要确保上一次 DMA 发送已经完成,要避免重复请求。
  n. V& t# M, d" s! b3 r6 N3 H
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);
}
. h5 b2 D; Z$ v# q; @4 v8 N/ y
在 DMA 中断中判断是否发生了 DMA 传输完成事件,如果 DMA 传输完成则将相应标志位置位,并清除标志。
3 V0 j3 \5 |3 y
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);//显示字符

: z. K) D% Q1 ~5 [2 B) D. J
在 main 函数中进行过外设初始化之后,对 LCD 进行初始化,然后分别用四种颜色清屏,最后显示测试字符。
: I! v( c4 u7 J( q; p2 E. F5 c
实验现象:
下载烧录后可以观察到屏幕分别刷新蓝红绿白四种颜色,最后显示测试字符STM32G474Test。
" l. u; x- _& f+ E! }
! A0 p) K  a1 Z- u3 X* M$ c
七、IIC实验

5 P) s* ]  O3 S, E- r. h: I
实验目的:掌握和熟悉 IIC 软件模拟和硬件控制的使用和配置方法。
- w$ i& I; m; I" m6 c9 w
1、软件模拟 IIC 驱动 24C02 实验
CubeMX 配置如下,保存后生成对应的配置代码:

- L6 p$ X/ Z8 p3 m4 d* K. J/ U1 U
4.png
% b6 Z- A! T; ]5 C) y

3 j- @: S# m+ W# {2 ^( V' w
▲ CubeMX 进行软件 IIC 的 IO 配置

; Z0 T# a0 k' @$ N: k& A2 H3 S0 n, g
本实验使用软件 IIC 模拟,只需要配置 IO,初始 IO 配置都配置为输出 IO 即可,24C02 外围电路有上拉电阻,不需要配置内部上拉。
) K* Y& K0 u( V0 g3 e2 R
相关操作函数说明:
void SDA_Input_Mode()
功能:将 SDA 切换到输入模式;
参数:无;
返回:无;
说明:SDA 是双向的,在进行数据读取时需要切换到输入模式

! Z, o3 s7 @5 j
void SDA_Output_Mode()
功能:将 SDA 切换到输出模式;
参数:无;
返回:无;
说明:SDA 是双向的,在进行数据发送时需要切换到输出模式
# @  r+ V9 P9 j! p3 \: i5 j/ X
void I2CStart(void)
功能:模拟 IIC 的起始信号;
参数:无;
返回:无;
) Y+ J, ^) `/ X! c: I- A1 L! G
void I2CStop(void)
功能:模拟 IIC 的停止信号;
参数:无;
返回:无;
! u9 Z+ N8 r  n% c2 Z3 m* e
unsigned char I2CWaitAck(void)
功能:模拟 IIC 等待应答;
参数:无;
返回:应答结果,ERROR 或 SUCCESS;

* q& F) i" h2 ]9 z6 z
void I2CSendAck(void)
功能:模拟 IIC 的应答信号;
参数:无;
返回:无;

, Z. Z- X6 m$ `4 K4 V0 e8 O
void I2CSendNotAck(void)
功能:模拟 IIC 的非应答信号;
参数:无;
返回:无;
7 S) J  ?" q  n; s
void I2CSendByte(unsigned char cSendByte)
功能:通过模拟 IIC 发送一个字节;
参数:需要发送的字节;
返回:无;

1 g5 l; i6 p- g8 j* A9 Q8 k
unsigned char I2CReceiveByte(void)
功能:通过模拟 IIC 接收一个字节;
参数:无;
返回:接收到的字节;

5 ]6 E# s" w  `6 f
核心代码:
//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();//停止信号
}

2 l: N% O1 X: [3 X
上述两个函数为 24C02 的读写函数,写器件地址为 0xA0,读器件地址为 0xA1,地址由外部电路连接决定。

7 _4 U% x4 \; ~# r, a
I2CInit();
uint32_t i;
- ~* r" p+ ]- N9 p
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);

+ ^$ y8 L9 L& b+ i7 A  {3 y
//从 0x00 内存地址读出数据
for(i = 0; i < DataSize; i++)
Data_R=x24c02_read(i);
printf(" 24C02 Read ok\r\n");
: I( B2 l4 y% }3 d6 t% Z. x; D
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");
}
) p! `% d$ t6 g$ j! D1 K3 t3 _
以上为 main 函数中外设初始化结束后的部分,通过软件模拟 IIC 向 24C02 内存地址写入一段设定好的数据,然后将这段数据读出,最后进行对比。

' N9 L' N/ T* I9 H7 E7 x9 y1 D# N# K" O3 Z
实验现象:
下载烧录后可以观察到上位机串口助手打印测试数据。
7 J3 d4 [4 \$ U+ m8 ^
微信图片_20241122152540.png
8 h5 |* t' B: y% [4 W! @' x$ p

/ A- Y5 X$ M. L: `1 H# r3 q# A
▲ 实验现象

7 i( m/ i1 z  u% @: t1 Q) w7 Q
1 u) U! r# V1 h( h8 p! Q5 x
2、硬件 IIC 驱动 24C02 实验

9 E2 `: z. R* \4 u- d2 w
CubeMX 配置如下,保存后生成对应的配置代码:
( `5 H4 c/ @- h  y  Z' A' x0 k( l
微信图片_20241122152543.png : j0 J" B( S# [7 B8 g, e0 v& F
, B" \. Z: j0 d. @$ ]+ X' T% M
▲ CubeMX 进行 IIC 配置
  ~& a' _9 g: `! D* X) G# H# S
本实验使用硬件 IIC,启用之后 IIC 的配置不需要改变。
4 c$ i: C  \! W: o" P0 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;

* x: f5 Z$ T9 x$ y' U
示例:
HAL_I2C_Mem_Write(&hi2c3,Addr_W,0x01,I2C_MEMADD_SIZE_8BIT,Data_T,DataSize,0xFF);//通过 IIC 向目标器件的 0x01 地址写入待发送数据;

: S  F4 R' \# Z; f6 {1 ~# c/ r
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 地址读取数据;

8 _# a2 T: _" p/ v; L% p8 ?

, A6 @. `- @5 W, O4 J
核心代码:
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");
}

/ S. Y; }1 q( a+ G; s2 ^
以上为 main 函数中外设初始化结束后的部分,通过硬件 IIC 向 24C02 内存地址写入一段设定好的数据,然后将这段数据读出,最后进行对比。

3 E, p. C# g6 R! g9 v3 O. ?( F: V
实验现象:
下载烧录后可以观察到上位机串口助手打印测试数据。

- w: z5 j! G+ o  \3 z+ c
微信图片_20241122152546.png
% n5 r6 |, ^, @2 {
▲ 实验现象
6 l, m+ s9 a# }9 e
4 q0 B& ~8 V8 j% Q
七、ADC实验

5 x/ X5 R8 B& ?5 s- x
实验目的:掌握和熟悉 ADC 单路采集和多路采集的使用和配置方法,包含查询,中断,DMA等方式。
. `0 d) ?. G6 P: V6 e$ p
1、ADC 查询方式单路采集实验

6 H# D" g- N, M2 F2 I# u
CubeMX 配置如下,保存后生成对应的配置代码:

9 Q3 ?1 w/ G1 E+ N2 Z
微信图片_20241122152549.png
▲ CubeMX 进行 ADC 配置

2 S! ]- V( R8 }
本实验进行单通道 ADC 软件触发采样,只需要对 ADC 进行简单配置即可,同时使用串口进行数据输出,串口与时钟系统配置上文已经展示,参照上文实验进行配置。
1 U8 V& @$ U) P( E  i6 {( t; r: u
相关操作函数说明:
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 _  o# S' j7 v
HAL_StatusTypeDef HAL_ADC_Start(ADC_HandleTypeDef *hadc)
功能:使能 ADC,开启 ADC 规则组转换;
参数 1:ADC 句柄,根据实际需要填写;
返回:操作结果,HAL_OK,HAL_ERROR,HAL_BUSY;
示例:HAL_ADC_Start(&hadc1); //开启 ADC1 转换
注意:如果不是工作在连续模式,运行一次该函数进行一次转换
, K" ]) N/ V) m0 n* {# x" E, N
HAL_StatusTypeDef HAL_ADC_Stop(ADC_HandleTypeDef *hadc)
功能:关闭 ADC,停止 ADC 规则组转换;
参数 1:ADC 句柄,根据实际需要填写;
返回:操作结果,HAL_OK,HAL_ERROR;

! M0 o) s4 r4 H/ A0 X) D- Y. E
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); //等待转换完成

0 w( Y% H6 s0 g& P) {( N3 [
uint32_t HAL_ADC_GetValue(const ADC_HandleTypeDef *hadc)
功能:读取 ADC 规则组转换结果;
参数 1:ADC 句柄,根据实际需要填写;
返回:转换结果,ADC 采样寄存器值;
示例:ADC_Value = HAL_ADC_GetValue(&hadc1);// 读取转换的 AD 值

$ D. K8 ^; D0 l& [$ K$ Q5 L& D) \
核心代码:
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);
}
, }4 _9 {% t5 Y- L
以上为 main 函数中外设初始化结束后的部分,首先对 ADC 进行校准,然后进入主循环,循环中开启 ADC 转换,等待转换完成后读取转换结果,然后将结果转换为浮点数电压值,最后通过串口打印至 PC,每 50ms 进行一次测量。

7 L; \2 h( {# f% [* f  M
实验现象:
下载烧录后可以观察到上位机串口助手打印 ADC 测量数据。
$ j8 j% ^* Q6 Z7 {. ^
微信图片_20241122152553.png 8 A1 S, T; H6 b0 f0 @& S
0 b* W+ j8 k" O2 o
▲ 实验现象

$ D8 A( j+ Z$ U- t6 n

' k! ~$ J' z' ~0 q2 v& k. p
2、ADC 中断方式单路采集实验
& H& O1 @; M1 b$ Z; A
CubeMX 配置如下,保存后生成对应的配置代码:
% |; X6 i- D6 A& @3 ^# A
微信图片_20241122152556.png
▲ CubeMX 进行中断配置
8 B1 p) L8 q7 T4 S8 e
CubeMX 中的 ADC 基本配置与上例相同,这里需要开启 ADC1 的中断。
# Z: R/ k: R4 w6 c
相关操作函数说明:
HAL_StatusTypeDef HAL_ADC_Start_IT(ADC_HandleTypeDef *hadc)
功能:使能 ADC,以中断开启 ADC 规则组转换;
参数 1:ADC 句柄,根据实际需要填写;
返回:操作结果,HAL_OK,HAL_ERROR;
示例:HAL_ADC_Start_IT(&hadc1); //开启 ADC1 转换
注意:在 ADC 转换完成之后会触发中断,中断中读取采样数据
  T4 c8 v2 E" ^7 J
HAL_StatusTypeDef HAL_ADC_Stop_IT(ADC_HandleTypeDef *hadc)
功能:关闭 ADC,停止规则组转换,关闭转换结束中断;
参数 1:ADC 句柄,根据实际需要填写;
返回:操作结果,HAL_OK,HAL_ERRORT;
" `: T  D. T. V9 c  b4 W
核心代码:
HAL_ADCEx_Calibration_Start(&hadc1,ADC_SINGLE_ENDED); //矫正
while (1)
{
HAL_ADC_Start_IT(&hadc1); //中断方式启动 ADC
HAL_Delay(50);
}
  E! M6 x% V1 Q  |; C; f& J. a
以上为 main 函数中外设初始化结束后的部分,首先对 ADC 进行校准,然后进入主循环,循环中中断模式开启 ADC 转换,每 50ms 进行一次测量。
2 z5 r1 N2 l2 e. D* j8 Z
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); //通过串口发送
}
}
- j! l1 l5 b4 _! m7 T& m
以上为 ADC 转换完成中断回调函数,该函数为 ADC 共用的,进入此函数首先要判断是哪个 ADC 转换完成了,然后读取相应 ADC 的数据寄存器,转换为浮点数电压,通过串口发送到上位机。
# B" \7 O: ^; X* A( w/ u9 u; _
实验现象:
下载烧录后可以观察到上位机串口助手打印 ADC 测量数据。
$ ^8 x, M, |0 B$ M
微信图片_20241122152600.png 7 w) ?5 M& G) W3 `" p; N
0 s! c8 n" O+ W. P
▲ 实验现象

, x" w7 u) u2 i0 f6 Y
3、ADC 使用 DMA 方式单路采集实验
CubeMX 配置如下,保存后生成对应的配置代码:
" w  H8 o$ E& a: d. n
微信图片_20241122152607.png
▲ CubeMX 进行 ADC 配置

7 b4 w1 O; j$ B- n6 ?5 T; y
微信图片_20241122152610.png 9 A( `2 k# Y) x/ ^/ ?& r  J
▲ CubeMX 进行 DMA 配置
+ J+ X7 q. z7 s  y
CubeMX 中的 ADC 基本配置需要开启连续转换模式,使能 DMA 请求,然后需要对 ADC1的 DMA 进行配置,使用连续传输模式,半字传输。

+ ~9 }1 N+ y# }) m+ Q+ i  }8 N
相关操作函数说明:
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 传输;
6 T$ ~3 s3 B0 h, S) J/ T* Q
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

* p+ d$ L$ A% A+ D. B
核心代码:
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();
}

  Q8 L9 U# ~' t6 n1 V  N# j+ M( }
以上为 main 函数中外设初始化结束后的部分,首先对 ADC 进行校准,然后开启 ADC,使用 DMA 进行传输。
0 v; |7 i1 ]& q8 O  x
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 转换。
+ s+ D; I+ i. S
void DMA1_Channel1_IRQHandler(void)
{
ADC_DMA_Handle();
HAL_DMA_IRQHandler(&hdma_adc1);
}
4 q% b! g- d6 s1 Q# ]
以上为 DMA 中断处理函数,在其中添加 ADC_DMA_Handle();。
" p4 D$ B# ?2 T* a  {% n
实验现象:
下载烧录后可以观察到上位机串口助手打印 ADC 测量数据。

3 y$ R( D5 E/ F  d6 L3 f0 J& H
微信图片_20241122152613.png " ~3 P: c. M5 b9 E
& N0 F. \$ @2 d3 f9 B) c( a
▲ 实验现象
: K7 p  \; z$ S' l
4、内部温度采集实验
! X- J  }; e" W% m
CubeMX 配置如下,保存后生成对应的配置代码:
5 G: `" R( c  A: t
微信图片_20241122152616.png 4 n5 E! w/ B( ?6 x

9 W# _( l/ ?: D* D/ D4 J* ~
▲ CubeMX 进行温度传感器 ADC 配置
4 z' i6 R6 _2 L. ~1 i
本实验进行内部温度传感器读取,需要注意采样时间需要给足,手册要求最小采样时间 5us,根据时钟频率进行换算。

7 b% I5 G: `7 ?  {6 j6 h( ]( S; K
相关操作函数说明:
__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);//转换温度
, ~2 @0 \) w. P
核心代码:
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);
}

  a7 E# l3 D% W6 S
以上为 main 函数中外设初始化结束后的部分,首先对 ADC 进行校准,然后进入主循环,循环中开启 ADC 转换,等待转换完成后读取转换结果,然后将调用自带的温度转换函数将 ADC 采样值转换为温度,最后通过串口打印至 PC,每 500ms 进行一次测量。

9 X& H2 _5 @  T* R
实验现象:
下载烧录后可以观察到上位机串口助手打印温度测量数据。

. o; X" F4 c; Z; e, F2 o1 u
微信图片_20241122152619.png 5 p; N1 g8 t4 \6 O1 u, o: Q
▲ 实验现象
& j) \4 U) _+ j& }
5、VABT 电压采集实验
CubeMX 配置如下,保存后生成对应的配置代码:
4 P6 U( J5 c7 F, [' M
微信图片_20241122152621.png 8 }0 g: L# p- I; ?) Z+ X
% j# o) w. w2 S' a7 H1 C) {" G2 J( e
▲ CubeMX 进行 ADC 配置

9 Q5 k! H4 U- S4 a
本实验进行 VBAT 电压读取,基本配置与例 3.8.1 相同,需要注意采样时间需要给足,手册要求最小采样时间 12us,根据时钟频率进行换算。

- p# N5 r5 w! I. Q1 |6 J: S" d
核心代码:
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);
}

6 s0 r2 i3 Y2 d  ?; A; E
以上为 main 函数中外设初始化结束后的部分,首先对 ADC 进行校准,然后进入主循环,循环中开启 ADC 转换,等待转换完成后读取转换结果,然后将结果转换为浮点数电压值,需要注意的是,VBAT 采样在内部进行了 1/3 分压,因此最终电压计算结果需要乘 3,最后通过串口打印至 PC,每 500ms 进行一次测量。

6 M9 g/ g+ W% Q1 k9 B
实验现象:
下载烧录后可以观察到上位机串口助手打印 ADC 测量数据。

  e$ f& `) h% Q3 Z. `/ e
微信图片_20241122152624.png
▲ 实验现象
2 F7 l: j: y4 b
6、内部基准电压采集实验
CubeMX 配置如下,保存后生成对应的配置代码:
微信图片_20241122152627.png # a7 T" ^& O' `1 r

+ |+ k# f# M; W5 p
▲ CubeMX 进行温度传感器 ADC 配置
2 j! k* V% n& S% k+ u( V1 F) Y( Y
本实验进行内部参考电压读取,基本配置与例 3.8.1 相同,需要注意采样时间需要给足,手册要求最小采样时间 4us,根据时钟频率进行换算,读取之后通过内部参考电压反算外部参考电压。

5 e: u7 ?, @7 W# W6 @
相关操作函数说明:
__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+

/ }) b# ~; u# @* Z% E
核心代码:
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);
}

* J7 A( w1 y  K2 j) U
以上为 main 函数中外设初始化结束后的部分,首先对 ADC 进行校准,然后进入主循环,循环中开启 ADC 转换,等待转换完成后读取转换结果,然后将调用自带的电压转换函数将内部参考电压 ADC 采样值转换为实际外部参考电压输入,最后通过串口打印至 PC,每500ms 进行一次测量。

' i  q" h! G( K
实验现象:
下载烧录后可以观察到上位机串口助手打印外部参考电压测量数据。
6 j9 n8 [6 J, F( f5 m0 _6 V
微信图片_20241122152630.png 5 D* {. U% y2 h" x
& A1 `/ M" B- s2 i% c  J
▲ 实验现象

' s8 _3 |, C" r2 A% R. Y/ q
7、定时器触发单通道 ADC 采样
CubeMX 配置如下,保存后生成对应的配置代码:
微信图片_20241122152633.png
& r# r, w( a! x4 W, D5 `
5 ]; E9 \8 `9 \* r' V
▲ CubeMX 进行 ADC 触发配置
微信图片_20241122152635.png + g# _+ g, {$ k# T; `# a+ {( G
2 N/ K7 a. b4 c0 f: J; ]
▲ CubeMX 进行定时器配置
微信图片_20241122152638.png
& H! r$ [' c: F; F2 a1 C1 d

  V, }2 E% R! T) u7 z& \
▲ CubeMX 进行中断配置

( P" ?7 \. J2 k. E
CubeMX 中的 ADC 基本配置单通道采样相同,这里需要开启 ADC1 的中断,并且修改转换触发源,原来的软件触发改为使用定时器时间进行触发,TIM1 配置周期为 10ms,即每 10ms触发一次 ADC 转换。

7 Z) Z. U) v; G+ i' [; g" K
相关操作函数说明:
HAL_StatusTypeDef HAL_ADC_Start_IT(ADC_HandleTypeDef *hadc)
功能:使能 ADC,以中断开启 ADC 规则组转换;
参数 1:ADC 句柄,根据实际需要填写;
返回:操作结果,HAL_OK,HAL_ERROR;
示例:HAL_ADC_Start_IT(&hadc1); //开启 ADC1 转换
注意:在 ADC 转换完成之后会触发中断,中断中读取采样数据
! U( ~2 q+ [  p7 D: ?2 Q3 b
HAL_StatusTypeDef HAL_ADC_Stop_IT(ADC_HandleTypeDef *hadc)
功能:关闭 ADC,停止规则组转换,关闭转换结束中断;
参数 1:ADC 句柄,根据实际需要填写;
返回:操作结果,HAL_OK,HAL_ERRORT;
9 ?4 P' k4 X1 y4 A1 g% T
核心代码:
HAL_ADCEx_Calibration_Start(&hadc1,ADC_SINGLE_ENDED); //矫正
HAL_ADC_Start_IT(&hadc1);//中断方式启动 ADC
HAL_TIM_Base_Start(&htim1);//启动 TIM1
( a! W/ @7 r5 I$ @: H2 H3 z, x
以上为 main 函数中外设初始化结束后的部分,首先对 ADC 进行校准,然后中断方式开启ADC 转换,这里主要是要开启 ADC 并且使能中断,然后开启 TIM1,通过 TIM 触发 ADC进行转换。
3 S5 M' R; J9 {* g1 V
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); //通过串口发送
}
}
% q% l3 r8 `$ T
以上为 ADC 转换完成中断回调函数,该函数为 ADC 共用的,进入此函数首先要判断是哪个 ADC 转换完成了,然后读取相应 ADC 的数据寄存器,转换为浮点数电压,通过串口发送到上位机。

! H% y& g; }* ^  h6 _# L  C
实验现象:
下载烧录后可以观察到上位机串口助手打印 ADC 测量数据。
6.png
+ u- K) O+ u7 J3 U. p) Q) I

- a2 i  t$ {( Y2 ~( r  O( c! k
▲ 实验现象
九、DAC实验

+ f% F6 w1 {" ^+ Q
实验目的:掌握和熟悉 DAC 单路输出的软件触发和定时器触发配置方法,配合 DMA 输出波形。

% c3 t" F) Q2 H( K! c1 o2 g7 c
1、DAC 软件触发输出实验
9 `' u; f4 `% R0 R5 Q% k
CubeMX 配置如下,保存后生成对应的配置代码:

7 z0 n0 F0 O5 A6 E- b4 m# f
微信图片_20241122152645.png 3 U7 I. b( M7 E) C
- ~. }) Z: V0 a" h% o& K
▲ CubeMX 进行 DAC 输出配置
! s* \6 F7 O: [1 `8 S4 S, U
本实验进行软件触发 DAC 输出,开启 DAC1 的 OUT1 输出,使用外部输出引脚,使用普通模式,并且使能输出缓冲,将触发设置为软件触发。

4 ^2 M3 y; I  x6 g& B$ e4 @: P
相关操作函数说明:
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 输出,如果想要修改生效,还需要使用下面的函数

. f" ]$ v$ ?, v+ r2 \
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);// 改变输出值! u: S7 o) S( x! h4 I

# Z$ I  m+ F; _" ?- e; N
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;

1 X4 p7 [3 N( j
核心代码:
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);// 延时一毫秒
}
}
4 G- ~5 j2 B+ Q5 X
以上为 main 函数中外设初始化结束后的部分,主循环中根据正弦表切换 DAC 电压输出,1ms 进行一次切换,正选表一共 60 个点。
+ e$ k1 J8 i  w- l0 V$ P
实验现象:下载烧录后可以观察到 PA4 输出一个正弦波,频率约为 8.333Hz。

5 t( ^# L! `: E( a
微信图片_20241122165909.png , _4 ]$ A* ?! j; M# O

* {  @" S& Q3 ~8 v7 v! p* {5 A
▲ 实验现象

: n, t! R1 E3 j3 L
2、定时器触发 DMA 传输 DAC 输出实验
# ]3 I, B  u- y  I" i; Z
CubeMX 配置如下,保存后生成对应的配置代码:
微信图片_20241122152650.png , F* M) R( ^4 G3 i1 b6 ^* Z

% a  @; H* Z7 i3 q) Z
▲ CubeMX 进行 DAC 基本配置
微信图片_20241122152652.png # j$ c( D$ Y- D% @( R  Q0 x2 `

/ e0 \* ~. \+ A" T. ~: ~
▲ CubeMX 进行 DMA 配置
微信图片_20241122152654.png
- U& O0 z! W; |
$ |; E' _& O/ w" Q9 ?
▲ CubeMX 进行 TIM4 配置
; m* W1 c8 H  x8 G' k* V. s- f
本实验进行定时器触发 DAC 输出,开启 DAC1 的 OUT1 输出,使用外部输出引脚,使用普通模式,并且使能输出缓冲,将触发设置为 TIM4 触发,配置 DMA,使用循环模式,整字传输,配置 TIM4,设置定时器周期为 1ms。

) H% J: B2 v* H% i# 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 方式设置输出值

5 |7 Q  h/ N6 d5 F. Y
核心代码:
//正弦表
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
+ _7 G/ ]; B* Z, k
以上为正弦表定义。
2 c1 G8 @+ v" l
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();
}

% R4 ^" B6 c& s  J6 E/ d
以上为 main 函数中外设初始化结束后的部分,开启 TIM4 进行触发,以 DMA 方式开启DAC 转换输出。
8 J) n' d9 J" a, H
实验现象:
下载烧录后可以观察到 PA4 输出一个正弦波,频率约为 12.5Hz。
微信图片_20241122152657.png " c' Z2 _$ H% M/ z3 @
; X& L1 A7 |+ _; q& p$ T
▲ 实验现象

4 w9 I" A" J+ p- x2 b0 G
3、定时器触发 DAC 输出噪声实验
* G) \2 J0 q$ R4 P. F" S
CubeMX 配置如下,保存后生成对应的配置代码:
微信图片_20241122152659.png
3 A7 O% t* B9 P8 J& P8 U. F
▲ CubeMX 进行 DAC 输出配置
微信图片_20241122152701.png
- e7 r5 l! V2 x/ e6 p1 E3 t
* }2 Q2 P, d; A% i- \, Y4 e/ W
▲ CubeMX 进行 TIM2 配置
- ]- H7 o2 p* Z  l& e  r  D5 V
本实验使用 TIM2 触发 DAC 进行输出,输出内容由 DAC 随机生成,产生噪声。

% `% `* P3 k# x% ]) h* r2 G
核心代码:
HAL_DAC_Start(&hdac1,DAC_CHANNEL_1);//启动 DAC 输出
HAL_TIM_Base_Start(&htim2);//启动 TIM2 触发 DAC

6 o- v- x" R# A
以上为 main 函数中外设初始化结束后的部分,只需要开启 DAC 输出和定时器即可。

- R2 i+ P$ \) j5 y
实验现象:
下载烧录后可以观察到 PA4 输出随机噪声。
微信图片_20241122152703.png
4 m% v1 G, A) ~8 f
▲ 实验现象

1 b& j7 o/ J' _' a
如有侵权请联系删除
# t, ^3 \& b. o/ M0 T4 \; {
转载自:AI电堂/ c! f% Z3 S2 v6 D- f' N2 N( n
. c% n. l1 \; n) n( j
赞 收藏 评论0 发布时间:2024-11-22 15:27

举报

0个回答

所属标签

相似技术帖

官网相关资源

关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
人形机器人运动控制、感知与智能配电
半导体创新技术与应用方向
EE架构与软件定义汽车
12V/48V 汽车智能配电(SPD)
区域控制单元(ZCU)与分区架构
关注我们
st-img 微信公众号
st-img 手机版