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

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

[复制链接]
攻城狮Melo 发布时间:2024-11-22 15:27
六、SPI 实验
. l# g  e* ]2 N( d4 S$ @实验目的:掌握和熟悉 SPI 软件模拟和硬件控制的使用和配置方法。
1 L, N  a8 J2 a! j  w9 ^. Y5 A
9 H6 o1 t" c0 o# }8 p, d! N
1、软件模拟 SPI 驱动 TFT 实验
( @1 o* L( y# x4 P0 s# @CubeMX 配置如下,保存后生成对应的配置代码:' Q! j8 K. o! r" g- l
13.png 0 w! P/ b  p$ \

/ Q& N0 F6 ]5 b& q/ R+ B& Q
▲ CubeMX 配置

, \' J! i0 \& ~5 T$ M: J- {
本实验使用软件模拟 SPI,只需要对相应 IO 进行配置即可,注意需要配置 IO 速度等级,CLK 信号和 SDA 信号频率较高,需要配置为 very high。
4 _' w& I- ~$ w6 [
: M4 k2 K  ^' r2 ]' N7 [8 P4 {) R
相关操作函数说明:
$ a( y" K" b+ x5 m, s8 N- hvoid Lcd_Reset(void)
" O' k8 ]. v. r6 i3 h

; Y: Y& |$ H0 {/ @5 `功能:液晶硬复位函数;
5 g) F% `7 j0 q# X参数:无;) x5 v  o1 L" X# p7 n/ m+ ^
返回:无;
9 U  o- m& k* b4 r" X5 i$ c说明:液晶初始化前需执行一次复位操作, T+ j2 l! F" u
void LCD_Initial(void)/ f9 X$ G, V) _, j( \5 l
功能:初始化液晶;, X1 g# B+ ]0 y" x0 M# K9 @" {
参数:无;1 H0 z' F" M' T. R- F+ U+ i8 I- ?) ?
返回:无;. V6 d: c2 \8 W- F/ a% E( t
说明:在对液晶写入内容前需要进行初始化配置;
* `6 J* u9 Z' Kvoid Lcd_ColorBox(unsigned int xStart,unsigned int yStart,unsigned int xLong,unsigned int yLong,unsigned int Color)
7 w' g% K0 E4 W3 V$ ?# @" P

2 }9 B- ^# C/ }1 A. G功能:Lcd 矩形填充函数;9 [4 d5 R* F+ p  j0 i  e
参数 1:x 方向的起始点;: Z9 ^+ a  x: U. Z6 ~8 q
参数 2:y 方向的起始点;
+ W3 S; b2 S+ `参数 3:x 方向的长度;
0 E: s6 ?/ |" _2 y参数 4:y 方向的长度;
1 R& Z2 g9 F) J2 \- @9 ]+ Y1 E; K; L8 v参数 5:填充的颜色;9 R4 V$ o# W1 r1 c/ B
返回:无;! u9 B7 J) i+ @8 d6 ~* n
说明:将指定区域内填充指定颜色,常用于清屏
) ^& Q+ {0 `' Fvoid BlockWrite(unsigned int Xstart,unsigned int Xend,unsigned int Ystart,unsigned int Yend)
2 f' g. z5 r' \
( |( X4 ]$ j: }0 X% X功能:在一个指定位置开一个矩形框;
) E) s' ]! P. C" S' D; o参数 1:x 方向的起始点;
0 E3 ~4 @' c  j参数 2:x 方向的终点;$ a1 F8 G7 F. }5 V; x" Z7 n
参数 3:y 方向的起始点;1 h8 G) W1 a4 \2 q) I' @6 W
参数 4:y 方向的终点;# v, I3 v# [5 g7 c% q4 v  Z
返回:无;0 `  F' ?! y5 w9 V2 D
说明:开一个矩形框,方便接下来往这个框填充数据;7 b, t- S% Y! i6 x* m" r) @7 `
void DrawPixel(unsigned int x, unsigned int y, int Color)
$ @) E6 E, a8 I- p$ y  v功能:在 x,y 坐标上打一个颜色为 Color 的点;
( q- j  ~6 n/ h9 v9 P参数 1:x 坐标;
: n, y+ f% M4 n* B参数 2:y 坐标;
' y8 y, ]3 k" J- D4 X) w  S参数 3:点的颜色;3 V  n) m' w2 K0 y0 m
返回:无;" ~  X' ]: A1 w) m0 ~6 U
void LCD_PutString(unsigned short x, unsigned short y, char *s, unsigned int fColor, unsigned int bColor,unsigned char flag)
2 Z0 z5 ^3 V( ]8 b- N# V7 n$ z* o! }* A( k6 D' r
功能:显示一个字符串;4 o( X# f# [0 N& ~% y
参数 1:起始点 x 坐标;
2 ]/ L# t( d) M4 N9 o6 ^% S. x: f参数 2:起始点 y 坐标;! K. L- a+ M6 R# z9 C# z3 d# n# x  w
参数 3:字符串指针;; b( [& t! T7 k7 a& S
参数 4:前景色;
; @4 F: b: ?+ y, G/ ~3 ?/ o- U参数 5:背景色;; O! V* T7 {7 z5 w! a
参数 6:有无背景色;
; `$ p7 _5 G7 O/ _& c/ Q$ B返回:无/ \) C& ]8 u8 t. d2 u( F$ A  {0 Q$ y

& H! _' d, B$ d+ F- h9 p/ R) ?
/ f5 T; B, u( L- B7 t/ `2 I% h( D
核心代码:& H" z3 k, K) ^
LCD_Initial();
/ Y4 {0 _* A% T' w+ gLcd_ColorBox(0,0,XSIZE_PHYS,YSIZE_PHYS,Blue);//用蓝色清屏. w- P! j  \. y
Lcd_ColorBox(0,0,XSIZE_PHYS,YSIZE_PHYS,Red);//用红色清屏% }+ k. N$ q- S9 \( I
Lcd_ColorBox(0,0,XSIZE_PHYS,YSIZE_PHYS,Green);//用绿色清屏# _6 ~9 g7 @" j8 D  {
Lcd_ColorBox(0,0,XSIZE_PHYS,YSIZE_PHYS,White);//用白色清屏
6 \7 C4 L) i5 W3 J; ]0 P$ wLCD_PutString(10,10,"STM32G474Test",Red,White,0);//显示字符
. N3 i8 p8 j8 B( H& n5 P. [# A1 B8 u8 i( \0 V4 d: `' \. k! |3 ]
在 main 函数中进行过外设初始化之后,对 LCD 进行初始化,然后分别用四种颜色清屏,最后显示测试字符。- h. o! H9 U5 l! x- u7 a' D- J6 j/ \
- q7 j1 U! M5 \4 x2 C. G9 q" {

8 I" l5 w0 L4 n: a1 C实验现象:
$ a) u) C5 G6 A3 M4 J- {( m: O+ I下载烧录后可以观察到屏幕分别刷新蓝红绿白四种颜色,最后显示测试字符STM32G474Test。2 H: E$ e0 ~/ n" O) A: j. Q# Q: }
% O! S: p+ ?% i0 ]
7 l$ ~, r2 q) `# c- O
2、硬件 SPI 驱动 TFT 实验
6 J1 c. T3 c; M- oCubeMX 配置如下,保存后生成对应的配置代码:
' E5 W4 h1 b( K

4 R- n6 w  ~  Q/ ]( A& q) W+ \+ Q
12.png
/ x! j+ h5 D7 v$ n8 P8 a7 c
" }# J+ I) a- Q
▲ CubeMX 进行 SPI 配置
微信图片_20241122152531.png
0 O5 Z+ ~' G9 Y0 o; I# Z
2 u; B! b$ b. J4 T6 n
▲ CubeMX 进行 IO 速度配置
( h% N8 }0 c& ]' X/ K! @5 d
本实验使用硬件 SPI,需要配置 SPI 的时钟分频,配置出合适的时钟速率,另外需要注意设置时钟信号的空闲电平以及采样边沿,还需要将高速的信号 IO 速度进行配置,其他 IO配置与软件模拟 SPI 相同。

$ ~. o# o7 b, p; [8 m
$ Z  v- H6 x* I5 D7 _
; X& \6 P) C" ~( f1 y/ V' W

+ ?8 H$ g3 a2 s( x7 R
相关操作函数说明:

& \$ [8 O. k1 ^! \; z" W5 N3 E9 ~
HAL_StatusTypeDef HAL_SPI_Transmit(SPI_HandleTypeDef *hspi, uint8_t *pData, uint16_t Size, uint32_t Timeout)
4 i+ N* p0 L* R0 Y4 j9 b
功能:通过硬件 SPI 发送一组数据;
2 F' T- _/ M: V# R( ]6 ~
参数 1:SPI 句柄,根据实际需要填写;
; L* K/ }/ P5 q) L
参数 2:要发送数据的指针,常见为发送数据数组的首地址;
- ~2 Y9 w) z" V
参数 3:发送数据长度,单位字节;
; _) y% _: Y, ~* l: H) N9 V4 i
参数 4:发送超时时间,单位 ms;

" j8 a( a% Q' M' f2 U
返回:操作结果,HAL_OK,HAL_ERROR;

6 H& l/ r# T" K8 _- ]9 x: Y# a1 a
示例:HAL_SPI_Transmit ( &hspi4,data_color,2*xLong,10 );//通过 SPI4 发送颜色数据

" O: d; ]% i+ s
HAL_StatusTypeDef HAL_SPI_Receive(SPI_HandleTypeDef *hspi, uint8_t *pData, uint16_t Size, uint32_t Timeout)

. _* A* i' ~& l; _, b
功能:通过硬件 SPI 接收一组数据;
# r( n9 @7 Z) m$ w8 q' x
参数 1:SPI 句柄,根据实际需要填写;
, g0 Z7 r6 r/ Y7 j7 U- Z
参数 2:要接收数据保存指针;

$ P8 K- g6 B5 K
参数 3:接收数据长度,单位字节;
: {1 x% q+ f2 ]( n! H5 T
参数 4:接收超时时间,单位 ms;

1 T! `3 Q/ e  w+ r& |' Z3 g
返回:操作结果,HAL_OK,HAL_ERROR;
9 }7 q; v5 z7 h
HAL_StatusTypeDef HAL_SPI_TransmitReceive(SPI_HandleTypeDef *hspi, uint8_t *pTxData, uint8_t *pRxData, uint16_t Size, uint32_t Timeout)

$ o3 v7 |& u" N% v) z+ B
功能:通过硬件 SPI 交换一组数据;

9 h7 W* |: Z% i# T- S
参数 1:SPI 句柄,根据实际需要填写;

6 K- F' R, W8 q) H/ X; G( Y9 ]
参数 2:要发送数据的指针,常见为发送数据数组的首地址;
9 k) E+ i7 H( @; y0 T0 Y0 Y; E
参数 3:要接收数据的指针,接收数据数组的首地址;

7 s" |% p8 q8 N, }  L5 z
参数 4:数据长度,单位字节;
7 D; _, |0 N6 z7 E' n8 I$ ~, h
参数 5:超时时间,单位 ms;
: {  S6 ^1 F& x, c7 _
返回:操作结果,HAL_OK,HAL_ERROR;
6 L) t1 L+ H1 @
; X& l& t! _9 _- e$ s! u, J+ j& a

1 C2 q% F7 R4 s4 d; C
核心代码:
3 g9 ^1 q4 R8 D
LCD_Initial();

/ G- s8 e+ ?6 D0 i! V$ @( w
Lcd_ColorBox(0,0,XSIZE_PHYS,YSIZE_PHYS,Blue);//用蓝色清屏
- ^! Y' i; |; v% g8 b
Lcd_ColorBox(0,0,XSIZE_PHYS,YSIZE_PHYS,Red);//用红色清屏
( b* D" h# g* e) v' r, ]
Lcd_ColorBox(0,0,XSIZE_PHYS,YSIZE_PHYS,Green);//用绿色清屏

# l8 K! B" V1 b
Lcd_ColorBox(0,0,XSIZE_PHYS,YSIZE_PHYS,White);//用白色清屏
5 z' g" I0 O$ d8 l9 v
LCD_PutString(10,10,"STM32G474Test",Red,White,0);//显示字符
% t% U/ w5 p# i5 e6 x! h

/ h; s& J. A$ x; x
4 \# Q8 K' O& K& y% e8 X1 k$ ?
在 main 函数中进行过外设初始化之后,对 LCD 进行初始化,然后分别用四种颜色清屏,最后显示测试字符。

) a' @; ?) g( |# g! N/ J) R

) [# `4 q( Q: {1 S' a

( L' Y4 c) M8 @5 }. y/ H
实验现象:

) l7 X  \9 C( c- I
下载烧录后可以观察到屏幕分别刷新蓝红绿白四种颜色,最后显示测试字符STM32G474Test。

# O; `; k2 Y6 u! k; U% ~9 F$ e
) Z' `! H2 ?+ S% Z
% ~: O' v2 M9 V  X0 m- {
3、硬件 SPI 驱动 TFT 实验(DMA)

* x" o( U9 {; b% g: r2 I  F, g9 F
CubeMX 配置如下,保存后生成对应的配置代码:
微信图片_20241122152534.png

& W) P$ _3 g8 L9 i- B, p
▲ CubeMX 进行 SPI 的 DMA 配置
本实验使用硬件 SPI,使用 DMA 进行发送。

) p/ K1 z) a# k# l3 O1 l! ~4 G- 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)
; |" i" I! u( i# n5 f$ e
功能:通过硬件 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)
  r% t; d5 L% U! I2 |* ?# J
功能:通过硬件 SPI 的 DMA 方式交换一组数据;
参数 1:SPI 句柄,根据实际需要填写;
参数 2:要发送数据的指针,常见为发送数据数组的首地址;
参数 3:要接收数据的指针,接收数据数组的首地址;
参数 4:数据长度,单位字节;
返回:操作结果,HAL_OK,HAL_ERROR;
注意:使用相应 DMA 时需要对该 DMA 请求进行配置;
* t$ Z- s$ S$ ?1 q
核心代码:
//发送函数修改
if((temp+1) % xLong == 0)
{
HAL_SPI_Transmit_DMA(&hspi4,data_color,2*xLong);
while(!dma_flag_temp);
dma_flag_temp = 0;
}
# |! ?7 P3 J* l* ^1 a* D
使用 DMA 方式进行发送时需要确保上一次 DMA 发送已经完成,要避免重复请求。

/ ^( h+ {5 Z8 t3 _) ]+ F! s
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);
}

( E$ W! A5 Y& i+ y# i
在 DMA 中断中判断是否发生了 DMA 传输完成事件,如果 DMA 传输完成则将相应标志位置位,并清除标志。

4 l  J7 j& U0 a
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);//显示字符

7 B$ ~$ p# d, O7 b
在 main 函数中进行过外设初始化之后,对 LCD 进行初始化,然后分别用四种颜色清屏,最后显示测试字符。

" r( q# s. t; ?. B2 a7 A
实验现象:
下载烧录后可以观察到屏幕分别刷新蓝红绿白四种颜色,最后显示测试字符STM32G474Test。
& w( J% `# x, l1 A: m( w. [2 b: \; D

2 o7 w' U8 v$ m, S0 f5 P$ v1 \: a
七、IIC实验
: C9 w6 Y* I2 ]5 V& g) m/ u# b" s& i
实验目的:掌握和熟悉 IIC 软件模拟和硬件控制的使用和配置方法。

6 k7 p5 v0 R3 {  l4 W
1、软件模拟 IIC 驱动 24C02 实验
CubeMX 配置如下,保存后生成对应的配置代码:
! T+ C# Z3 j* Z; o: b
4.png : f* Y% h/ L- D) I3 m" t: q
; k0 F8 o7 F  r7 U
▲ CubeMX 进行软件 IIC 的 IO 配置

. P+ {* j. \. u! U* A
本实验使用软件 IIC 模拟,只需要配置 IO,初始 IO 配置都配置为输出 IO 即可,24C02 外围电路有上拉电阻,不需要配置内部上拉。
+ [6 i; b) K2 K
相关操作函数说明:
void SDA_Input_Mode()
功能:将 SDA 切换到输入模式;
参数:无;
返回:无;
说明:SDA 是双向的,在进行数据读取时需要切换到输入模式

& @7 J  T; l! |) ?; \7 ^* \
void SDA_Output_Mode()
功能:将 SDA 切换到输出模式;
参数:无;
返回:无;
说明:SDA 是双向的,在进行数据发送时需要切换到输出模式
# _* m, s) l- @4 I4 m# C
void I2CStart(void)
功能:模拟 IIC 的起始信号;
参数:无;
返回:无;
2 i4 v+ ]( v5 F  A* G! ^
void I2CStop(void)
功能:模拟 IIC 的停止信号;
参数:无;
返回:无;

5 K8 Q0 {+ N0 T6 h3 ^& u
unsigned char I2CWaitAck(void)
功能:模拟 IIC 等待应答;
参数:无;
返回:应答结果,ERROR 或 SUCCESS;

+ `' t4 p$ q9 g: l  T
void I2CSendAck(void)
功能:模拟 IIC 的应答信号;
参数:无;
返回:无;
+ @1 Q: R, b! n1 \
void I2CSendNotAck(void)
功能:模拟 IIC 的非应答信号;
数:无;
返回:无;
0 e; T# k$ C" r
void I2CSendByte(unsigned char cSendByte)
功能:通过模拟 IIC 发送一个字节;
参数:需要发送的字节;
返回:无;
* x* v7 j2 L: c. A: l
unsigned char I2CReceiveByte(void)
功能:通过模拟 IIC 接收一个字节;
参数:无;
返回:接收到的字节;

6 F  e- {; I* F) a+ @* Q' {% V3 J
核心代码:
//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();//停止信号
}
" c6 l7 \9 D  E' j; C& J# [0 P6 F0 u
上述两个函数为 24C02 的读写函数,写器件地址为 0xA0,读器件地址为 0xA1,地址由外部电路连接决定。

% T: Y6 `. ~* u4 o
I2CInit();
uint32_t i;

+ ?" [+ m% l7 \3 \, H7 V
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);
3 D$ Y+ V6 C! s& U7 Q$ G
//从 0x00 内存地址读出数据
for(i = 0; i < DataSize; i++)
Data_R=x24c02_read(i);
printf(" 24C02 Read ok\r\n");

/ F  N) e0 T9 N3 A7 d: u
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");
}

$ m# u: S/ o9 M) y6 O' S! p8 ~
以上为 main 函数中外设初始化结束后的部分,通过软件模拟 IIC 向 24C02 内存地址写入一段设定好的数据,然后将这段数据读出,最后进行对比。

4 ?+ q+ u! O, g6 {- P; D
实验现象:
下载烧录后可以观察到上位机串口助手打印测试数据。
6 W9 U" I& D% k9 p2 G
微信图片_20241122152540.png
8 @) O. C8 N1 N' S5 w* F

0 ~3 S5 {) P3 _3 B8 R7 o1 t
▲ 实验现象

' V$ J% k4 m8 D* s  y4 o

" Q3 i, {" e( ]4 H
2、硬件 IIC 驱动 24C02 实验
7 E2 y0 o2 P6 @5 P5 \7 w
CubeMX 配置如下,保存后生成对应的配置代码:
. g4 D) E, }1 N8 Z; g
微信图片_20241122152543.png ; W. j% Q+ N: s( Y

" i2 s; n& {% G' p. D% T' m
▲ CubeMX 进行 IIC 配置
1 E) x5 K+ W2 }9 y" `% [  X
本实验使用硬件 IIC,启用之后 IIC 的配置不需要改变。

" W8 K) M9 B6 s/ S/ f7 {
相关操作函数说明:
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;

( n+ s. h& o! ?* Y. P& c  l9 P
示例:
HAL_I2C_Mem_Write(&hi2c3,Addr_W,0x01,I2C_MEMADD_SIZE_8BIT,Data_T,DataSize,0xFF);//通过 IIC 向目标器件的 0x01 地址写入待发送数据;
2 A/ j0 c: l  L- N
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 地址读取数据;

9 E4 S3 r! M! T2 m# L7 x. Y
  u( y' X- O1 I0 i' n# t3 w
核心代码:
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");
}

4 t; E, O  K2 G2 Y9 C% i
以上为 main 函数中外设初始化结束后的部分,通过硬件 IIC 向 24C02 内存地址写入一段设定好的数据,然后将这段数据读出,最后进行对比。

2 o- ~" S! D  N; w
实验现象:
下载烧录后可以观察到上位机串口助手打印测试数据。

5 ]4 ]" @  `7 b% V0 |
微信图片_20241122152546.png ) q. B2 C$ i: U* j* }) x8 v  s
▲ 实验现象
" Y0 _. p% M. Y7 [

2 E# K3 {, H$ x% Q$ Q! k  v' y: P
七、ADC实验
  p" f( O) f7 k, n; j9 Q
实验目的:掌握和熟悉 ADC 单路采集和多路采集的使用和配置方法,包含查询,中断,DMA等方式。
: i; w: D$ x' I2 Y& X$ Y! N2 \4 e
1、ADC 查询方式单路采集实验

6 p3 C5 I3 W; B5 a3 p# v% h
CubeMX 配置如下,保存后生成对应的配置代码:

* A! F3 |+ x: I% ?* ~: h& a3 N
微信图片_20241122152549.png
▲ CubeMX 进行 ADC 配置
7 }- W1 l/ B# t) v) U
本实验进行单通道 ADC 软件触发采样,只需要对 ADC 进行简单配置即可,同时使用串口进行数据输出,串口与时钟系统配置上文已经展示,参照上文实验进行配置。

9 j/ k$ q0 G& {/ L+ Y
相关操作函数说明:
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 进行单端采样模式下的校准;
9 [  ]; F" S$ j4 @, }
HAL_StatusTypeDef HAL_ADC_Start(ADC_HandleTypeDef *hadc)
功能:使能 ADC,开启 ADC 规则组转换;
参数 1:ADC 句柄,根据实际需要填写;
返回:操作结果,HAL_OK,HAL_ERROR,HAL_BUSY;
示例:HAL_ADC_Start(&hadc1); //开启 ADC1 转换
注意:如果不是工作在连续模式,运行一次该函数进行一次转换
" M) n4 h: O( ^- G% ?
HAL_StatusTypeDef HAL_ADC_Stop(ADC_HandleTypeDef *hadc)
功能:关闭 ADC,停止 ADC 规则组转换;
参数 1:ADC 句柄,根据实际需要填写;
返回:操作结果,HAL_OK,HAL_ERROR;
7 S- K, M! L9 S" ^3 l: R
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); //等待转换完成

9 a/ W8 M! X. z4 @
uint32_t HAL_ADC_GetValue(const ADC_HandleTypeDef *hadc)
功能:读取 ADC 规则组转换结果;
参数 1:ADC 句柄,根据实际需要填写;
返回:转换结果,ADC 采样寄存器值;
示例:ADC_Value = HAL_ADC_GetValue(&hadc1);// 读取转换的 AD 值
3 N# O" E8 }+ ?+ b, |
核心代码:
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);
}

. L: M+ h! n" {) j0 }
以上为 main 函数中外设初始化结束后的部分,首先对 ADC 进行校准,然后进入主循环,循环中开启 ADC 转换,等待转换完成后读取转换结果,然后将结果转换为浮点数电压值,最后通过串口打印至 PC,每 50ms 进行一次测量。
7 P# s4 M: F/ K2 g" @* O, `
实验现象:
下载烧录后可以观察到上位机串口助手打印 ADC 测量数据。
  {  n2 a- v6 s
微信图片_20241122152553.png ; ~/ I5 L# M! B2 [. |+ i- ~$ x

/ Q. q/ @% c3 ]1 h4 L* w' U
▲ 实验现象
2 Y! y8 A( N0 N% \

  j+ }' I* f  [* D; F
2、ADC 中断方式单路采集实验
, m' W1 N! P6 T# k2 f
CubeMX 配置如下,保存后生成对应的配置代码:
% F* a/ {% g8 E8 U
微信图片_20241122152556.png
▲ CubeMX 进行中断配置
$ L5 [7 H/ A9 X/ \3 z
CubeMX 中的 ADC 基本配置与上例相同,这里需要开启 ADC1 的中断。

( A: [* l* n& t4 t
相关操作函数说明:
HAL_StatusTypeDef HAL_ADC_Start_IT(ADC_HandleTypeDef *hadc)
功能:使能 ADC,以中断开启 ADC 规则组转换;
参数 1:ADC 句柄,根据实际需要填写;
返回:操作结果,HAL_OK,HAL_ERROR;
示例:HAL_ADC_Start_IT(&hadc1); //开启 ADC1 转换
注意:在 ADC 转换完成之后会触发中断,中断中读取采样数据
) ^% w8 c5 T; x! I! X7 m
HAL_StatusTypeDef HAL_ADC_Stop_IT(ADC_HandleTypeDef *hadc)
功能:关闭 ADC,停止规则组转换,关闭转换结束中断;
参数 1:ADC 句柄,根据实际需要填写;
返回:操作结果,HAL_OK,HAL_ERRORT;

) d' v* |) `% S. @4 \6 a/ K* P* l
核心代码:
HAL_ADCEx_Calibration_Start(&hadc1,ADC_SINGLE_ENDED); //矫正
while (1)
{
HAL_ADC_Start_IT(&hadc1); //中断方式启动 ADC
HAL_Delay(50);
}
5 e& x7 x, W% R7 v  s" f- X% `& P
以上为 main 函数中外设初始化结束后的部分,首先对 ADC 进行校准,然后进入主循环,循环中中断模式开启 ADC 转换,每 50ms 进行一次测量。

# p; }6 U- k. h) A3 z% 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); //通过串口发送
}
}

- ]3 X7 E* ]4 Z
以上为 ADC 转换完成中断回调函数,该函数为 ADC 共用的,进入此函数首先要判断是哪个 ADC 转换完成了,然后读取相应 ADC 的数据寄存器,转换为浮点数电压,通过串口发送到上位机。
; l* Q! f9 R3 A7 Y* |0 F
实验现象:
下载烧录后可以观察到上位机串口助手打印 ADC 测量数据。

  B6 A% E1 Z; `& o6 X- Q( m
微信图片_20241122152600.png
6 j; ?8 i- ]: K& Z2 G! n) ~' I' z
) q# m. O! j/ n; Q, F9 }* V
▲ 实验现象

8 O7 I1 W5 A2 w3 ^6 _9 x! K0 A
3、ADC 使用 DMA 方式单路采集实验
CubeMX 配置如下,保存后生成对应的配置代码:

- T7 \; ~' k* n  Q% D+ ^
微信图片_20241122152607.png
▲ CubeMX 进行 ADC 配置
& \! r, a2 t* f# Q" Q  _  F
微信图片_20241122152610.png 9 p+ Z: c- _$ W3 ^( R! N$ q: @
▲ CubeMX 进行 DMA 配置

, c# n/ V3 f$ Y
CubeMX 中的 ADC 基本配置需要开启连续转换模式,使能 DMA 请求,然后需要对 ADC1的 DMA 进行配置,使用连续传输模式,半字传输。

  ^& c/ ^; L2 H9 m: v9 q
相关操作函数说明:
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 传输;

- {$ `8 r; m; h# z) R0 o
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
  T* ~9 l% o0 d" \
核心代码:
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();
}

8 U& e" ^* h- ^+ i% ~
以上为 main 函数中外设初始化结束后的部分,首先对 ADC 进行校准,然后开启 ADC,使用 DMA 进行传输。
3 B. n+ e8 ]) e) e: E; o
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 转换。

5 u* p( J5 \( P7 b! p
void DMA1_Channel1_IRQHandler(void)
{
ADC_DMA_Handle();
HAL_DMA_IRQHandler(&hdma_adc1);
}

; _' M" w) }0 T+ J: c/ m/ _6 V: ~
以上为 DMA 中断处理函数,在其中添加 ADC_DMA_Handle();。

" ~+ v# Y5 o8 e/ ^# |" O
实验现象:
下载烧录后可以观察到上位机串口助手打印 ADC 测量数据。
( B) F, l/ Y+ H, T# x; x
微信图片_20241122152613.png
/ k9 z- ?& y& }! w8 F: k, l- \

2 `2 Z: h3 H( o* S% I
▲ 实验现象

7 ?( c; [4 z8 a( z8 F' c( ^
4、内部温度采集实验
2 K8 N2 c$ Z% a
CubeMX 配置如下,保存后生成对应的配置代码:
. [/ O- i( V+ K
微信图片_20241122152616.png % T, J% w3 Q4 X* E

& A8 l2 P; r! t+ w0 m* j  j& v9 H
▲ CubeMX 进行温度传感器 ADC 配置

" w( [7 W$ R0 D" [% K+ z* m
本实验进行内部温度传感器读取,需要注意采样时间需要给足,手册要求最小采样时间 5us,根据时钟频率进行换算。
% n/ J! Z9 L. ]6 O( P( i! W
相关操作函数说明:
__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);//转换温度

- l/ o+ e7 ~6 g: L. [
核心代码:
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);
}

9 \$ h" [* L6 v4 y- b- F# N
以上为 main 函数中外设初始化结束后的部分,首先对 ADC 进行校准,然后进入主循环,循环中开启 ADC 转换,等待转换完成后读取转换结果,然后将调用自带的温度转换函数将 ADC 采样值转换为温度,最后通过串口打印至 PC,每 500ms 进行一次测量。

# C6 r' e7 k& h
实验现象:
下载烧录后可以观察到上位机串口助手打印温度测量数据。

* W( c& P& T5 z4 g. S, f5 Y1 V
微信图片_20241122152619.png 4 W- m. x/ L* L, y: {9 U# {
▲ 实验现象
( f- U: y$ m$ t5 L0 H( b
5、VABT 电压采集实验
CubeMX 配置如下,保存后生成对应的配置代码:
, H- m3 t) g2 `% X/ H& ~
微信图片_20241122152621.png 8 H- b/ o0 R3 w- I1 {

/ H2 K' F9 @- H" H6 I6 I
▲ CubeMX 进行 ADC 配置

, F9 `; }5 s# a$ w
本实验进行 VBAT 电压读取,基本配置与例 3.8.1 相同,需要注意采样时间需要给足,手册要求最小采样时间 12us,根据时钟频率进行换算。

* |- ]% F' h& @1 U' V, j* S
核心代码:
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);
}
8 a3 C) L. W6 U# m
以上为 main 函数中外设初始化结束后的部分,首先对 ADC 进行校准,然后进入主循环,循环中开启 ADC 转换,等待转换完成后读取转换结果,然后将结果转换为浮点数电压值,需要注意的是,VBAT 采样在内部进行了 1/3 分压,因此最终电压计算结果需要乘 3,最后通过串口打印至 PC,每 500ms 进行一次测量。
* ~  b- n5 }1 O8 A# f
实验现象:
下载烧录后可以观察到上位机串口助手打印 ADC 测量数据。

/ \) f6 O. T4 L0 W* R: H
微信图片_20241122152624.png
▲ 实验现象

% \/ X$ l5 f4 k; B0 G$ `  A: ~$ [
6、内部基准电压采集实验
CubeMX 配置如下,保存后生成对应的配置代码:
微信图片_20241122152627.png 7 K, Q' k- j) r  q  [2 v$ Z5 N! _

. M4 w, M3 d2 b/ F
▲ CubeMX 进行温度传感器 ADC 配置
; t" ?9 Y- S0 n7 s
本实验进行内部参考电压读取,基本配置与例 3.8.1 相同,需要注意采样时间需要给足,手册要求最小采样时间 4us,根据时钟频率进行换算,读取之后通过内部参考电压反算外部参考电压。

. B+ Z' j! R4 A1 V2 o7 ^
相关操作函数说明:
__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+
* h0 [: G0 I  k1 ~7 ?* Z; y
核心代码:
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);
}

6 f( A/ p+ p: I; W
以上为 main 函数中外设初始化结束后的部分,首先对 ADC 进行校准,然后进入主循环,循环中开启 ADC 转换,等待转换完成后读取转换结果,然后将调用自带的电压转换函数将内部参考电压 ADC 采样值转换为实际外部参考电压输入,最后通过串口打印至 PC,每500ms 进行一次测量。
# _+ P1 j1 P7 Q- z
实验现象:
下载烧录后可以观察到上位机串口助手打印外部参考电压测量数据。
( u! V, e  i! u$ G
微信图片_20241122152630.png
9 y5 U+ z' V, d

# F, M- [  M4 a# a. F3 ~+ N
▲ 实验现象

- a  ]) b6 U1 o
7、定时器触发单通道 ADC 采样
CubeMX 配置如下,保存后生成对应的配置代码:
微信图片_20241122152633.png . @4 s. @- w# s, m
) E, T4 Y, `1 K, I
▲ CubeMX 进行 ADC 触发配置
微信图片_20241122152635.png ' A5 l$ {5 r) r( ^. d6 K$ G
: G- n7 `. z3 y2 r9 b
▲ CubeMX 进行定时器配置
微信图片_20241122152638.png
- f- ^! y! F- o9 F  ]( s6 [2 |4 h
" f( u5 ~: ?% ~& \, f# H6 [4 j
▲ CubeMX 进行中断配置
& V7 |: V: ]4 q9 ~. Z5 j8 o
CubeMX 中的 ADC 基本配置单通道采样相同,这里需要开启 ADC1 的中断,并且修改转换触发源,原来的软件触发改为使用定时器时间进行触发,TIM1 配置周期为 10ms,即每 10ms触发一次 ADC 转换。

9 ]) z% E+ l4 C$ n* e0 ^: A( [7 H% L$ 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 转换完成之后会触发中断,中断中读取采样数据

- ?5 H% s$ Y6 y+ T8 d
HAL_StatusTypeDef HAL_ADC_Stop_IT(ADC_HandleTypeDef *hadc)
功能:关闭 ADC,停止规则组转换,关闭转换结束中断;
参数 1:ADC 句柄,根据实际需要填写;
返回:操作结果,HAL_OK,HAL_ERRORT;
, x  Z* e: W$ J
核心代码:
HAL_ADCEx_Calibration_Start(&hadc1,ADC_SINGLE_ENDED); //矫正
HAL_ADC_Start_IT(&hadc1);//中断方式启动 ADC
HAL_TIM_Base_Start(&htim1);//启动 TIM1
* E! y5 G3 ^5 J( `1 O; Q9 ]; q8 R* \
以上为 main 函数中外设初始化结束后的部分,首先对 ADC 进行校准,然后中断方式开启ADC 转换,这里主要是要开启 ADC 并且使能中断,然后开启 TIM1,通过 TIM 触发 ADC进行转换。
" q" v( E" [) a" W
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); //通过串口发送
}
}
( x& d: j( ]$ n' y
以上为 ADC 转换完成中断回调函数,该函数为 ADC 共用的,进入此函数首先要判断是哪个 ADC 转换完成了,然后读取相应 ADC 的数据寄存器,转换为浮点数电压,通过串口发送到上位机。
& k' ]$ j* n) r
实验现象:
下载烧录后可以观察到上位机串口助手打印 ADC 测量数据。
6.png : h# k) O3 w/ l+ v6 g
* p5 i  R! N) {3 m
▲ 实验现象
九、DAC实验
& o* ]4 b9 m, n4 r6 j8 ~( r
实验目的:掌握和熟悉 DAC 单路输出的软件触发和定时器触发配置方法,配合 DMA 输出波形。
6 t9 f: g$ @- q3 V
1、DAC 软件触发输出实验

8 `* }% h5 @# A
CubeMX 配置如下,保存后生成对应的配置代码:

3 E% A4 }. S. c/ n! B2 V5 a& B& B1 V
微信图片_20241122152645.png
& E' }9 `: E! u9 J  A5 v' \

# _; |, `# |$ g
▲ CubeMX 进行 DAC 输出配置

5 O; R1 R: R: v+ z* Q, }
本实验进行软件触发 DAC 输出,开启 DAC1 的 OUT1 输出,使用外部输出引脚,使用普通模式,并且使能输出缓冲,将触发设置为软件触发。

+ w, m9 J) u$ y  w9 k# [. g
相关操作函数说明:
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 输出,如果想要修改生效,还需要使用下面的函数

' H) Z  ^7 ?. Z( W1 [
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);// 改变输出值; \3 x( o/ j4 h  c3 @
8 @' L8 u) a6 a$ G$ c
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;
. o/ j8 _/ y1 I5 F
核心代码:
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);// 延时一毫秒
}
}
' I% x, P+ s# Z2 D8 z
以上为 main 函数中外设初始化结束后的部分,主循环中根据正弦表切换 DAC 电压输出,1ms 进行一次切换,正选表一共 60 个点。

/ k( u) c1 o/ j' d4 ]" f
实验现象:下载烧录后可以观察到 PA4 输出一个正弦波,频率约为 8.333Hz。

3 P+ o5 _' Z$ ]* o0 D1 x% c
微信图片_20241122165909.png & }8 E9 }) s. M" y
! v! |% Q- z/ e, }/ t
▲ 实验现象
8 A2 U+ [# [) s  v7 a( S$ C
2、定时器触发 DMA 传输 DAC 输出实验
, l6 \, Y; @$ b9 L/ p
CubeMX 配置如下,保存后生成对应的配置代码:
微信图片_20241122152650.png # ?1 |2 m3 X5 f- k$ z, e
# P' g% e* d6 l& X
▲ CubeMX 进行 DAC 基本配置
微信图片_20241122152652.png
1 I7 I3 r8 D+ |5 e" l7 ?! P$ P; }
2 {& K' t/ \( F4 C( P
▲ CubeMX 进行 DMA 配置
微信图片_20241122152654.png
+ a& ~. E0 l7 U: o  q% }
$ T' i8 t. s4 y5 L. h0 F9 `
▲ CubeMX 进行 TIM4 配置
0 V% ?& t/ z1 Q
本实验进行定时器触发 DAC 输出,开启 DAC1 的 OUT1 输出,使用外部输出引脚,使用普通模式,并且使能输出缓冲,将触发设置为 TIM4 触发,配置 DMA,使用循环模式,整字传输,配置 TIM4,设置定时器周期为 1ms。
5 g& _  O# L% P4 Q( B: J& c, `
相关操作函数说明:
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 方式设置输出值
2 D8 f. r5 |. U6 K
核心代码:
//正弦表
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

9 e5 E" S; J! A) A. K3 n
以上为正弦表定义。
1 i8 ~: @8 C7 f6 k% a+ P1 E6 D: q0 X; V
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();
}
  j2 Q2 U9 m. t: u0 \6 Z5 W1 {3 d) \
以上为 main 函数中外设初始化结束后的部分,开启 TIM4 进行触发,以 DMA 方式开启DAC 转换输出。

8 X, B! P% |" m: c; @* p
实验现象:
下载烧录后可以观察到 PA4 输出一个正弦波,频率约为 12.5Hz。
微信图片_20241122152657.png
( v7 M* V$ d/ d+ |7 k& i4 q! Z

3 s1 e" M* ]: @4 H: z' J% z) i
▲ 实验现象
  U" \- u5 T9 f) y6 v0 F
3、定时器触发 DAC 输出噪声实验

* @6 C  d0 x7 V0 z+ H
CubeMX 配置如下,保存后生成对应的配置代码:
微信图片_20241122152659.png 2 U5 G. r) z" g6 E& W6 w, H
▲ CubeMX 进行 DAC 输出配置
微信图片_20241122152701.png
' E0 y5 N( b; m4 q- \3 X

+ T; J' j  x! C/ G2 H
▲ CubeMX 进行 TIM2 配置
; [4 ]7 J4 u; l  c: T
本实验使用 TIM2 触发 DAC 进行输出,输出内容由 DAC 随机生成,产生噪声。
: S% }' ?+ j2 q8 b/ {' D
核心代码:
HAL_DAC_Start(&hdac1,DAC_CHANNEL_1);//启动 DAC 输出
HAL_TIM_Base_Start(&htim2);//启动 TIM2 触发 DAC
) V1 Q- u; ?; N6 z: C
以上为 main 函数中外设初始化结束后的部分,只需要开启 DAC 输出和定时器即可。

% `: j" H* C. R3 R% n: h
实验现象:
下载烧录后可以观察到 PA4 输出随机噪声。
微信图片_20241122152703.png . b1 |* H5 Q6 \- o2 s  ?
▲ 实验现象

9 j# O* |7 f' o6 A
如有侵权请联系删除

8 c  K! r9 y# e6 Q, T, \) n
转载自:AI电堂
& r( u+ j# X9 ]/ S* B

% K) S- A, h: d* g
收藏 评论0 发布时间:2024-11-22 15:27

举报

0个回答

所属标签

相似分享

官网相关资源

关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
STM32N6 AI生态系统
STM32MCU,MPU高性能GUI
ST ACEPACK电源模块
意法半导体生物传感器
STM32Cube扩展软件包
关注我们
st-img 微信公众号
st-img 手机版