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

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

[复制链接]
攻城狮Melo 发布时间:2024-11-22 15:27
六、SPI 实验$ g! I( V" }& V, D! Q
实验目的:掌握和熟悉 SPI 软件模拟和硬件控制的使用和配置方法。
- `4 Y4 p" L- b8 E$ W

% L& E1 p+ [: }, z2 H% i! n1、软件模拟 SPI 驱动 TFT 实验
. \( i2 j$ f9 V/ iCubeMX 配置如下,保存后生成对应的配置代码:
6 J- W) a8 p- [; X
13.png
* @3 h1 {5 h& f5 U/ m7 Z+ h/ s

' w# r- M, B% W
▲ CubeMX 配置
+ g  E0 _& I) Z, E9 x
本实验使用软件模拟 SPI,只需要对相应 IO 进行配置即可,注意需要配置 IO 速度等级,CLK 信号和 SDA 信号频率较高,需要配置为 very high。+ `+ v' ~: i+ S% Q" `8 c

" g7 W" R5 t4 Q1 ]7 p/ J. y! {
相关操作函数说明:
3 r3 v( F" k7 W# {5 s" Avoid Lcd_Reset(void)0 h2 }4 U- I' R0 C$ g9 h

; Q2 T& N7 r5 a5 x# }功能:液晶硬复位函数;
6 f7 Q8 L( m, Q& P0 ]1 ]参数:无;
! o' V! a. c3 L+ k返回:无;
7 s) ]. C4 v3 o+ M说明:液晶初始化前需执行一次复位操作, t5 Y) j$ }! {1 q
void LCD_Initial(void)
2 |* E3 V! U, k0 i$ F* B. J! }功能:初始化液晶;
' W7 W+ {0 D! R2 x6 K参数:无;
) j7 P9 ?& Y" f1 I* i返回:无;7 j2 g, F+ L* i5 o' r
说明:在对液晶写入内容前需要进行初始化配置;/ R( d6 i1 v6 [, N$ s3 H7 L
void Lcd_ColorBox(unsigned int xStart,unsigned int yStart,unsigned int xLong,unsigned int yLong,unsigned int Color)
2 C8 r0 _% g5 ]

& K1 ]! Z+ R6 d3 X" z" W$ D2 ~! f功能:Lcd 矩形填充函数;, F1 ~& d, d* S9 u  N- y& g
参数 1:x 方向的起始点;
8 Z! g2 r- j7 e; h2 g参数 2:y 方向的起始点;4 X  e- h& p( i+ @' r1 ~
参数 3:x 方向的长度;  q5 P4 }, l0 m# X& o: e5 C* v" ]
参数 4:y 方向的长度;3 b6 P8 h) i9 U2 V
参数 5:填充的颜色;
" R; M; [2 Z' l% I返回:无;) c4 O* R- @9 w2 S+ p- c( L
说明:将指定区域内填充指定颜色,常用于清屏7 q; X: a' w+ K3 [: C. q
void BlockWrite(unsigned int Xstart,unsigned int Xend,unsigned int Ystart,unsigned int Yend)  ?0 u  J7 t' a4 Q. r

* G9 M* G6 E8 p% h功能:在一个指定位置开一个矩形框;
; h7 r! B/ k" b( J+ v( w参数 1:x 方向的起始点;7 b) l2 O1 c' h- \1 N
参数 2:x 方向的终点;
; i: i+ ?& ^  J1 p5 n6 c+ R参数 3:y 方向的起始点;
! w7 E1 Y, s: I) S2 U参数 4:y 方向的终点;2 x% U1 ?+ B7 _# v# N8 e: N
返回:无;
0 O( ^, I. Q4 h! z说明:开一个矩形框,方便接下来往这个框填充数据;2 V2 g4 P$ M7 |) b  |& ]
void DrawPixel(unsigned int x, unsigned int y, int Color)
3 G) F3 h0 ^- I5 C( P1 U( P  R功能:在 x,y 坐标上打一个颜色为 Color 的点;! A- ]: H- o% v. a9 x6 b; o$ B
参数 1:x 坐标;! Y6 n6 e2 I) \( {0 f8 `
参数 2:y 坐标;
- w# U) `6 I' a, b) ?* E参数 3:点的颜色;
% v; @$ I. D4 x8 P$ G: @" k返回:无;
# Z% l- J6 f1 ~% ?$ dvoid LCD_PutString(unsigned short x, unsigned short y, char *s, unsigned int fColor, unsigned int bColor,unsigned char flag)  \. t2 y% n0 G# D% o5 ]
- C4 ~0 I( D7 l$ F' ^( a; t
功能:显示一个字符串;
+ d' K$ {) L& Q3 S1 N+ a$ o9 n0 h! c& s参数 1:起始点 x 坐标;
8 V7 X9 |6 L$ s8 L3 `4 }/ u! h参数 2:起始点 y 坐标;2 d& {5 a* |+ z% A& b! o! I& B
参数 3:字符串指针;
# K4 d7 ]% G% y  ?. q) P9 j参数 4:前景色;
( ^8 x; w7 e8 L9 m( f9 ~参数 5:背景色;
" r; L/ C! p5 P2 D参数 6:有无背景色;0 h% c. }% e3 ?4 C4 g
返回:无6 i8 d' \" F" Q# T& |

/ t+ X( L9 o6 a0 q
2 Y% O/ h5 ~8 o/ Y: r
核心代码:
0 R' e4 D+ x: R# h% W8 H: zLCD_Initial();" i, O' m+ n, v9 H+ b. V9 t/ Y0 s
Lcd_ColorBox(0,0,XSIZE_PHYS,YSIZE_PHYS,Blue);//用蓝色清屏
9 s7 G* J, e- qLcd_ColorBox(0,0,XSIZE_PHYS,YSIZE_PHYS,Red);//用红色清屏
( R6 N+ s: _  n1 Z8 CLcd_ColorBox(0,0,XSIZE_PHYS,YSIZE_PHYS,Green);//用绿色清屏( X$ u( O- _+ [3 R
Lcd_ColorBox(0,0,XSIZE_PHYS,YSIZE_PHYS,White);//用白色清屏
; K- e+ \% Q+ i! v! J, kLCD_PutString(10,10,"STM32G474Test",Red,White,0);//显示字符
% D% a5 P" ?% j3 ~8 E
" I: [$ T5 T, m% q8 G在 main 函数中进行过外设初始化之后,对 LCD 进行初始化,然后分别用四种颜色清屏,最后显示测试字符。& D( p1 F* e* d" p  c

6 n0 I8 J- a2 {: ~8 T

4 E2 Z& f( G" |  o' Q7 z, j实验现象:
1 L4 ?2 K8 C/ l下载烧录后可以观察到屏幕分别刷新蓝红绿白四种颜色,最后显示测试字符STM32G474Test。
5 t  l3 S8 g) |9 z; X2 f4 I
1 R! U+ _- O; j) f& @' N
; l$ t1 A: B2 Q% c" y4 ]; ?
2、硬件 SPI 驱动 TFT 实验
6 h; |8 z0 w$ E4 R3 rCubeMX 配置如下,保存后生成对应的配置代码:
4 ?# A; B' P1 z7 V7 P- [

" I4 J, f' m9 v  x( j: b) G9 n
12.png + a; Z: w8 Z; B1 l. D9 D
3 o% T# t. r+ P9 D& N+ N9 Y! E
▲ CubeMX 进行 SPI 配置
微信图片_20241122152531.png # G5 k% g3 z/ `# `+ L! [( o. i$ d

# w- k' X) C, c
▲ CubeMX 进行 IO 速度配置" F: z7 Q/ m/ Z: t5 O
本实验使用硬件 SPI,需要配置 SPI 的时钟分频,配置出合适的时钟速率,另外需要注意设置时钟信号的空闲电平以及采样边沿,还需要将高速的信号 IO 速度进行配置,其他 IO配置与软件模拟 SPI 相同。

. r: i7 v: t) i$ T0 }! G
2 d8 t. f: \/ [0 K8 G2 j- q( O

9 m0 }5 d2 }6 B: z0 [

. s- `7 s, r: _3 H
相关操作函数说明:

: |+ }0 h; c' ^) |; R! K
HAL_StatusTypeDef HAL_SPI_Transmit(SPI_HandleTypeDef *hspi, uint8_t *pData, uint16_t Size, uint32_t Timeout)

$ u$ r  w/ `, ]" l8 Q
功能:通过硬件 SPI 发送一组数据;
+ V3 K. B4 c5 M* m: x! ~4 ?
参数 1:SPI 句柄,根据实际需要填写;

$ j6 t/ [, l' |- Y& _
参数 2:要发送数据的指针,常见为发送数据数组的首地址;
) `4 ^& j" O( [1 @' v1 ]
参数 3:发送数据长度,单位字节;
9 q; E9 r* I. g
参数 4:发送超时时间,单位 ms;

& ?  t4 l$ @: I0 _
返回:操作结果,HAL_OK,HAL_ERROR;
9 O% S+ z, f6 [; `
示例:HAL_SPI_Transmit ( &hspi4,data_color,2*xLong,10 );//通过 SPI4 发送颜色数据
/ J1 c, ]  {& b  J: `& a
HAL_StatusTypeDef HAL_SPI_Receive(SPI_HandleTypeDef *hspi, uint8_t *pData, uint16_t Size, uint32_t Timeout)
: t0 D8 {) F4 j7 F7 D# D. [
功能:通过硬件 SPI 接收一组数据;

0 m4 U" E$ o: I8 p; F- t. W  O
参数 1:SPI 句柄,根据实际需要填写;

" Z7 b6 F- Z0 U8 [9 w! {. P
参数 2:要接收数据保存指针;

) j* I+ p% h' r& b" Q
参数 3:接收数据长度,单位字节;
- M# J# g- q. {# z7 {" V  c
参数 4:接收超时时间,单位 ms;

& w2 x, K8 g0 g
返回:操作结果,HAL_OK,HAL_ERROR;

& f7 P6 d: Q2 o' s% E! u% _
HAL_StatusTypeDef HAL_SPI_TransmitReceive(SPI_HandleTypeDef *hspi, uint8_t *pTxData, uint8_t *pRxData, uint16_t Size, uint32_t Timeout)
+ X. K) x0 S/ q: ?& Z( E6 z
功能:通过硬件 SPI 交换一组数据;
0 ?6 D. h- P% z1 [
参数 1:SPI 句柄,根据实际需要填写;

7 _  X+ i# B0 L( h. y
参数 2:要发送数据的指针,常见为发送数据数组的首地址;

, f& F' X* S* O' K2 A$ h  \1 q
参数 3:要接收数据的指针,接收数据数组的首地址;

) G" d, [2 I1 @0 k% I$ t7 t* i% b
参数 4:数据长度,单位字节;
, [  A- l9 t; d! l9 P8 `% R
参数 5:超时时间,单位 ms;
  D% d. Q5 `. }" J6 V* q" V
返回:操作结果,HAL_OK,HAL_ERROR;
3 D4 \3 z/ v9 s

: o$ Q' D9 ]! A# e2 z7 Q

" @- \7 r% x  Q" J
核心代码:

4 T  |; v8 z7 Y; `3 h& \
LCD_Initial();

' X( {' f. s( }& o& v0 Q
Lcd_ColorBox(0,0,XSIZE_PHYS,YSIZE_PHYS,Blue);//用蓝色清屏
- ^" L8 }; k  x  p
Lcd_ColorBox(0,0,XSIZE_PHYS,YSIZE_PHYS,Red);//用红色清屏
4 m. d. q" k: H* M
Lcd_ColorBox(0,0,XSIZE_PHYS,YSIZE_PHYS,Green);//用绿色清屏

: _( P& T' n- O5 {
Lcd_ColorBox(0,0,XSIZE_PHYS,YSIZE_PHYS,White);//用白色清屏

3 v- N8 O$ k7 s2 s/ O
LCD_PutString(10,10,"STM32G474Test",Red,White,0);//显示字符

% y: \3 j; B+ X- b! C8 v1 B* o
7 f) }; I4 f8 ~# M8 H" L# g

* x3 f+ L5 a, H3 o. ]4 J
在 main 函数中进行过外设初始化之后,对 LCD 进行初始化,然后分别用四种颜色清屏,最后显示测试字符。
5 I8 p/ R5 X) r0 i

& C/ R$ K" ]0 r6 ]( K9 T, K+ G; {4 ?
& O' G; H3 N% b& t4 ^
实验现象:
. d0 C, b  a/ ~  E
下载烧录后可以观察到屏幕分别刷新蓝红绿白四种颜色,最后显示测试字符STM32G474Test。
# a6 [" N9 ~5 @8 E# u
8 U9 X9 p: ^% c! A+ x# l
, g+ K1 V" A) D  `3 C3 F
3、硬件 SPI 驱动 TFT 实验(DMA)

9 I8 W' W# E/ S) ~3 L2 n* ]' c- }
CubeMX 配置如下,保存后生成对应的配置代码:
微信图片_20241122152534.png

; V3 h. q; |$ z6 }
▲ CubeMX 进行 SPI 的 DMA 配置
本实验使用硬件 SPI,使用 DMA 进行发送。

# l  G! a- p8 G% g( q
相关操作函数说明:
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)
4 T( b2 T; x/ l
功能:通过硬件 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)
7 n* W# \; M5 V; Q& e1 p
功能:通过硬件 SPI 的 DMA 方式交换一组数据;
参数 1:SPI 句柄,根据实际需要填写;
参数 2:要发送数据的指针,常见为发送数据数组的首地址;
参数 3:要接收数据的指针,接收数据数组的首地址;
参数 4:数据长度,单位字节;
返回:操作结果,HAL_OK,HAL_ERROR;
注意:使用相应 DMA 时需要对该 DMA 请求进行配置;

2 V. Y$ v, r0 n
核心代码:
//发送函数修改
if((temp+1) % xLong == 0)
{
HAL_SPI_Transmit_DMA(&hspi4,data_color,2*xLong);
while(!dma_flag_temp);
dma_flag_temp = 0;
}
( C2 p5 M6 A5 f& S( ?/ W2 I  d
使用 DMA 方式进行发送时需要确保上一次 DMA 发送已经完成,要避免重复请求。
- o$ p& S1 `$ t: {& i
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);
}

6 ]. y! ?% ]1 a) R9 I1 {9 a
在 DMA 中断中判断是否发生了 DMA 传输完成事件,如果 DMA 传输完成则将相应标志位置位,并清除标志。
+ e  {- d3 G$ ^7 i' [0 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);//显示字符

- g- C- h  F8 N! H  }) l
在 main 函数中进行过外设初始化之后,对 LCD 进行初始化,然后分别用四种颜色清屏,最后显示测试字符。

0 }; n: {# g% p8 d8 t7 }8 w
实验现象:
下载烧录后可以观察到屏幕分别刷新蓝红绿白四种颜色,最后显示测试字符STM32G474Test。

7 W8 i% m- o0 e7 D& A0 r
) m4 m+ H: j4 Y4 M6 Q) {2 @
七、IIC实验

- A& ?. G2 P) S4 D# X6 ?. t' F
实验目的:掌握和熟悉 IIC 软件模拟和硬件控制的使用和配置方法。

4 ^: j7 B0 ~3 g, ?& l1 n8 J
1、软件模拟 IIC 驱动 24C02 实验
CubeMX 配置如下,保存后生成对应的配置代码:

! e0 [" Z% t$ [2 {/ {; ~% n# \
4.png # e" b5 z  Z$ g4 Q6 a, o; B
1 u) U& p9 j$ z, a0 q
▲ CubeMX 进行软件 IIC 的 IO 配置

) b0 S) Q0 A5 j
本实验使用软件 IIC 模拟,只需要配置 IO,初始 IO 配置都配置为输出 IO 即可,24C02 外围电路有上拉电阻,不需要配置内部上拉。

2 n; V. T' z6 B4 m
相关操作函数说明:
void SDA_Input_Mode()
功能:将 SDA 切换到输入模式;
参数:无;
返回:无;
说明:SDA 是双向的,在进行数据读取时需要切换到输入模式

, |* j  s- {) X+ t5 w$ a
void SDA_Output_Mode()
功能:将 SDA 切换到输出模式;
参数:无;
返回:无;
说明:SDA 是双向的,在进行数据发送时需要切换到输出模式
" u$ t7 U1 n2 u. Q) t# ]
void I2CStart(void)
功能:模拟 IIC 的起始信号;
参数:无;
返回:无;
( y1 U0 C! f' Z' `. z
void I2CStop(void)
功能:模拟 IIC 的停止信号;
参数:无;
返回:无;
* s: U, F* U- f, f; ~2 d
unsigned char I2CWaitAck(void)
功能:模拟 IIC 等待应答;
参数:无;
返回:应答结果,ERROR 或 SUCCESS;
* ^  X# }3 G" J8 b3 X  E3 u6 r% W
void I2CSendAck(void)
功能:模拟 IIC 的应答信号;
参数:无;
返回:无;

6 X2 Z2 s" n+ X6 @- E+ d
void I2CSendNotAck(void)
功能:模拟 IIC 的非应答信号;
参数:无;
返回:无;
* q6 K7 i( [6 r. W5 k5 m) S: i
void I2CSendByte(unsigned char cSendByte)
功能:通过模拟 IIC 发送一个字节;
参数:需要发送的字节;
返回:无;

3 T  L+ D6 L3 ~. M, n* |
unsigned char I2CReceiveByte(void)
功能:通过模拟 IIC 接收一个字节;
参数:无;
返回:接收到的字节;
! C( A1 x9 R. }  Y
核心代码:
//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();//停止信号
}

+ H% C2 u5 Q( a; V% X. n8 T& T
上述两个函数为 24C02 的读写函数,写器件地址为 0xA0,读器件地址为 0xA1,地址由外部电路连接决定。

1 q5 O( s5 }$ `5 R
I2CInit();
uint32_t i;
- M& f, w3 T: y; J. z7 k2 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);

( z6 q+ K4 R3 `! A
//从 0x00 内存地址读出数据
for(i = 0; i < DataSize; i++)
Data_R=x24c02_read(i);
printf(" 24C02 Read ok\r\n");

3 A% r) [9 C7 W# @% \
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 X: h8 Q6 A/ B; [" ~9 K1 c& I
以上为 main 函数中外设初始化结束后的部分,通过软件模拟 IIC 向 24C02 内存地址写入一段设定好的数据,然后将这段数据读出,最后进行对比。
* W( ?& w) F& h2 [7 o1 u5 ?& k* O
实验现象:
下载烧录后可以观察到上位机串口助手打印测试数据。

% Q' G& J; @+ R3 W: n
微信图片_20241122152540.png " F( y( N% @" r$ b$ m  W

% f! i2 I4 i. |* L+ ]8 n
▲ 实验现象
. Q/ C$ f' }/ ?6 c# Y$ o8 I) R  s
* u! [/ Z( A1 ]4 O
2、硬件 IIC 驱动 24C02 实验

. z  P! ^% ~+ k% j) ]: p. f! q
CubeMX 配置如下,保存后生成对应的配置代码:

9 r  t* q! j) C1 X. e9 F
微信图片_20241122152543.png 8 Q) o; S8 d# \" v4 d
0 T- G) ]; i! @5 Q! ^
▲ CubeMX 进行 IIC 配置

( p, e! m" J( J3 g
本实验使用硬件 IIC,启用之后 IIC 的配置不需要改变。

! `. t( C9 _8 k# y! l7 }6 P
相关操作函数说明:
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;

" Q* l7 J; ?$ g/ W2 R% g( h
示例:
HAL_I2C_Mem_Write(&hi2c3,Addr_W,0x01,I2C_MEMADD_SIZE_8BIT,Data_T,DataSize,0xFF);//通过 IIC 向目标器件的 0x01 地址写入待发送数据;
7 o! l' E0 h! j' r' v0 \/ v9 H6 p8 N% j
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 地址读取数据;

+ ~, w5 C+ @. H* w

2 \) d6 g, w: Q
核心代码:
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");
}
5 \3 u2 I- W0 z6 Y2 \3 T- _8 v
以上为 main 函数中外设初始化结束后的部分,通过硬件 IIC 向 24C02 内存地址写入一段设定好的数据,然后将这段数据读出,最后进行对比。
) M& D6 ~+ w1 F3 S
实验现象:
下载烧录后可以观察到上位机串口助手打印测试数据。
2 z0 I* C9 W) j1 J6 v8 `
微信图片_20241122152546.png , [, t2 m' z* S: c
▲ 实验现象
# D& b5 H! |3 `1 T
- W/ H: n6 P; g, G5 }
七、ADC实验
" c2 g% f: ~2 S
实验目的:掌握和熟悉 ADC 单路采集和多路采集的使用和配置方法,包含查询,中断,DMA等方式。

- A$ r9 [8 L, q5 H; f
1、ADC 查询方式单路采集实验
* S: s/ J4 Y$ _( D
CubeMX 配置如下,保存后生成对应的配置代码:

) w8 r3 X- l3 n/ b8 ?
微信图片_20241122152549.png
▲ CubeMX 进行 ADC 配置

( a6 _+ Q- H% e$ C! k( ?; k
本实验进行单通道 ADC 软件触发采样,只需要对 ADC 进行简单配置即可,同时使用串口进行数据输出,串口与时钟系统配置上文已经展示,参照上文实验进行配置。
4 Y6 M8 D- G- g. J3 s
相关操作函数说明:
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 进行单端采样模式下的校准;
5 J* J/ Z3 J9 C* b( u( E
HAL_StatusTypeDef HAL_ADC_Start(ADC_HandleTypeDef *hadc)
功能:使能 ADC,开启 ADC 规则组转换;
参数 1:ADC 句柄,根据实际需要填写;
返回:操作结果,HAL_OK,HAL_ERROR,HAL_BUSY;
示例:HAL_ADC_Start(&hadc1); //开启 ADC1 转换
注意:如果不是工作在连续模式,运行一次该函数进行一次转换
; h$ `* E0 F! m& J
HAL_StatusTypeDef HAL_ADC_Stop(ADC_HandleTypeDef *hadc)
功能:关闭 ADC,停止 ADC 规则组转换;
参数 1:ADC 句柄,根据实际需要填写;
返回:操作结果,HAL_OK,HAL_ERROR;
$ v3 w7 O+ @8 ?8 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); //等待转换完成
* V7 a$ N# R4 B+ _; K) s  c$ C( @
uint32_t HAL_ADC_GetValue(const ADC_HandleTypeDef *hadc)
功能:读取 ADC 规则组转换结果;
参数 1:ADC 句柄,根据实际需要填写;
返回:转换结果,ADC 采样寄存器值;
示例:ADC_Value = HAL_ADC_GetValue(&hadc1);// 读取转换的 AD 值

" I0 o5 W9 O; q& X+ @: W
核心代码:
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);
}
7 H4 B6 n$ W/ z( x1 g7 H
以上为 main 函数中外设初始化结束后的部分,首先对 ADC 进行校准,然后进入主循环,循环中开启 ADC 转换,等待转换完成后读取转换结果,然后将结果转换为浮点数电压值,最后通过串口打印至 PC,每 50ms 进行一次测量。
. _- x; s& D" ?9 Q6 P. U
实验现象:
下载烧录后可以观察到上位机串口助手打印 ADC 测量数据。

4 D, \# }! ~& }- T' L
微信图片_20241122152553.png
0 S! [. Y9 ~$ I8 w5 Y& ^

: P* x1 m) Y# R4 G. |
▲ 实验现象

" u3 N, m9 j% E; o

: c( E6 v  S: @! d( ?4 H
2、ADC 中断方式单路采集实验
5 E2 a5 s/ @( h
CubeMX 配置如下,保存后生成对应的配置代码:

9 m4 A6 L" L7 u/ ]6 w% O
微信图片_20241122152556.png
▲ CubeMX 进行中断配置
$ i% n+ k2 h) C1 s: b
CubeMX 中的 ADC 基本配置与上例相同,这里需要开启 ADC1 的中断。
1 z- V7 [3 E# l. Z. B2 T2 X9 A' L
相关操作函数说明:
HAL_StatusTypeDef HAL_ADC_Start_IT(ADC_HandleTypeDef *hadc)
功能:使能 ADC,以中断开启 ADC 规则组转换;
参数 1:ADC 句柄,根据实际需要填写;
返回:操作结果,HAL_OK,HAL_ERROR;
示例:HAL_ADC_Start_IT(&hadc1); //开启 ADC1 转换
注意:在 ADC 转换完成之后会触发中断,中断中读取采样数据
6 w: p" a8 R7 q* C; A2 m
HAL_StatusTypeDef HAL_ADC_Stop_IT(ADC_HandleTypeDef *hadc)
功能:关闭 ADC,停止规则组转换,关闭转换结束中断;
参数 1:ADC 句柄,根据实际需要填写;
返回:操作结果,HAL_OK,HAL_ERRORT;
# W7 n, M! Q1 U- v
核心代码:
HAL_ADCEx_Calibration_Start(&hadc1,ADC_SINGLE_ENDED); //矫正
while (1)
{
HAL_ADC_Start_IT(&hadc1); //中断方式启动 ADC
HAL_Delay(50);
}
2 r4 R6 w. t+ r" \
以上为 main 函数中外设初始化结束后的部分,首先对 ADC 进行校准,然后进入主循环,循环中中断模式开启 ADC 转换,每 50ms 进行一次测量。
) a7 Z# S; D$ F' u: Y9 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); //通过串口发送
}
}

" [2 o  i( R8 }! M3 _. m7 G
以上为 ADC 转换完成中断回调函数,该函数为 ADC 共用的,进入此函数首先要判断是哪个 ADC 转换完成了,然后读取相应 ADC 的数据寄存器,转换为浮点数电压,通过串口发送到上位机。

0 S% _$ T% S; {4 K+ ]/ z3 X
实验现象:
下载烧录后可以观察到上位机串口助手打印 ADC 测量数据。

; z, q# o! D8 k' c6 B2 ?
微信图片_20241122152600.png $ h' s6 z3 L% v) p: A, m5 o0 Q
; _; M5 o# g  p0 B% M7 J
▲ 实验现象

' \* c! x! @' J# z+ Q
3、ADC 使用 DMA 方式单路采集实验
CubeMX 配置如下,保存后生成对应的配置代码:
$ y+ X8 n. Z1 \; N& I9 T' K/ T
微信图片_20241122152607.png
▲ CubeMX 进行 ADC 配置
2 [/ }' @8 V2 h/ Q* p
微信图片_20241122152610.png " @  }' @, e0 `6 ]! \: z
▲ CubeMX 进行 DMA 配置

( ]  O+ K* ^% l+ }
CubeMX 中的 ADC 基本配置需要开启连续转换模式,使能 DMA 请求,然后需要对 ADC1的 DMA 进行配置,使用连续传输模式,半字传输。

) g" H3 S7 Z! u4 a, A# G$ Z/ C
相关操作函数说明:
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 传输;
1 q- P; {9 i7 u" F! y7 Z6 N
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

! k5 B9 S  \$ d8 q1 Q* J5 t
核心代码:
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();
}

2 M8 [. J5 w) R& p2 X
以上为 main 函数中外设初始化结束后的部分,首先对 ADC 进行校准,然后开启 ADC,使用 DMA 进行传输。
4 P# h. g) M5 ~1 T0 V
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 转换。

% j7 C2 z7 U/ M" r5 u2 @9 ]
void DMA1_Channel1_IRQHandler(void)
{
ADC_DMA_Handle();
HAL_DMA_IRQHandler(&hdma_adc1);
}
& m# }* |% b& l
以上为 DMA 中断处理函数,在其中添加 ADC_DMA_Handle();。
) o- T, s- d! c0 A  f
实验现象:
下载烧录后可以观察到上位机串口助手打印 ADC 测量数据。

/ J7 ~0 |3 f# `
微信图片_20241122152613.png
3 G& f8 i( B) h+ @# |
1 U+ |# z, K6 i3 D$ z& J
▲ 实验现象

' V. @# w- ~$ U6 q4 @' @
4、内部温度采集实验

. d' t+ [/ D5 e& y6 x! d1 _4 S* Q
CubeMX 配置如下,保存后生成对应的配置代码:

2 O" v, f+ ]4 q" H+ L" s
微信图片_20241122152616.png
" P3 F) [0 w2 t2 Y- N7 B- ~

9 ?/ b0 P/ L+ s; M7 \" x) H2 x
▲ CubeMX 进行温度传感器 ADC 配置

- j8 y0 t: v2 E, Q6 U2 }) R) T% W
本实验进行内部温度传感器读取,需要注意采样时间需要给足,手册要求最小采样时间 5us,根据时钟频率进行换算。
4 g4 S, J0 A" m; P8 I
相关操作函数说明:
__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);//转换温度

9 H' {- q2 N3 I9 b1 }( u8 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 值
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);
}
2 S9 i. Q' Q7 k' [+ t; ^9 D
以上为 main 函数中外设初始化结束后的部分,首先对 ADC 进行校准,然后进入主循环,循环中开启 ADC 转换,等待转换完成后读取转换结果,然后将调用自带的温度转换函数将 ADC 采样值转换为温度,最后通过串口打印至 PC,每 500ms 进行一次测量。
& ?* C4 @4 Q5 h  G/ N! U
实验现象:
下载烧录后可以观察到上位机串口助手打印温度测量数据。

2 Q: O  W* `) ^+ J
微信图片_20241122152619.png
7 U- O: D! b) n7 @0 J
▲ 实验现象
$ Z) r, F+ O4 {# _) O
5、VABT 电压采集实验
CubeMX 配置如下,保存后生成对应的配置代码:
$ m, C( M2 x! B" Y( _- b
微信图片_20241122152621.png
4 t, ~9 v- ]. C3 P  [' h
7 }3 {# j. N: F8 A7 x! y+ P
▲ CubeMX 进行 ADC 配置
0 `- K9 i% N& A; d/ r
本实验进行 VBAT 电压读取,基本配置与例 3.8.1 相同,需要注意采样时间需要给足,手册要求最小采样时间 12us,根据时钟频率进行换算。
, G' z. G+ J) P9 q9 y# `8 o
核心代码:
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);
}

1 n; h' L. q, n( R6 u/ z- P8 A/ i7 C
以上为 main 函数中外设初始化结束后的部分,首先对 ADC 进行校准,然后进入主循环,循环中开启 ADC 转换,等待转换完成后读取转换结果,然后将结果转换为浮点数电压值,需要注意的是,VBAT 采样在内部进行了 1/3 分压,因此最终电压计算结果需要乘 3,最后通过串口打印至 PC,每 500ms 进行一次测量。
1 F- K# U) ^% J) [9 `2 h
实验现象:
下载烧录后可以观察到上位机串口助手打印 ADC 测量数据。

  I. u) U+ S& a7 ~! j4 G) Z* V
微信图片_20241122152624.png
▲ 实验现象
7 f# G  l$ p8 z) e# M
6、内部基准电压采集实验
CubeMX 配置如下,保存后生成对应的配置代码:
微信图片_20241122152627.png
7 Y: k$ G0 R( \1 O+ @* Q) i+ E" m9 I
* Q5 P. R" v5 x/ V, b- Q  M) C
▲ CubeMX 进行温度传感器 ADC 配置

7 C8 |6 g- ]) b0 c+ @0 ]- _
本实验进行内部参考电压读取,基本配置与例 3.8.1 相同,需要注意采样时间需要给足,手册要求最小采样时间 4us,根据时钟频率进行换算,读取之后通过内部参考电压反算外部参考电压。
6 y8 j4 S2 L& Z( Y
相关操作函数说明:
__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+

. T3 i0 A4 a! J& k' E$ 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);
}

# l8 x4 k3 w6 N( k9 G
以上为 main 函数中外设初始化结束后的部分,首先对 ADC 进行校准,然后进入主循环,循环中开启 ADC 转换,等待转换完成后读取转换结果,然后将调用自带的电压转换函数将内部参考电压 ADC 采样值转换为实际外部参考电压输入,最后通过串口打印至 PC,每500ms 进行一次测量。
7 F% k$ i" h2 C& R% i
实验现象:
下载烧录后可以观察到上位机串口助手打印外部参考电压测量数据。
! c" |+ c7 e. C' A2 |3 y$ w
微信图片_20241122152630.png
+ H* h+ Z+ @6 l* B) p' X! W4 b, _) r4 |

, y: k! ]' i: ^6 R" Q2 m( C
▲ 实验现象
% R' d* o* _# B
7、定时器触发单通道 ADC 采样
CubeMX 配置如下,保存后生成对应的配置代码:
微信图片_20241122152633.png
: M! V5 k4 Z" q, \0 s

( Z2 W9 v; v* M, D4 F8 V
▲ CubeMX 进行 ADC 触发配置
微信图片_20241122152635.png
, o- V8 G$ c2 b3 D% N' s+ F
3 A  b) F4 b0 d
▲ CubeMX 进行定时器配置
微信图片_20241122152638.png
) _% {( B8 z' U2 o1 b

  `8 }4 R: @: J6 b
▲ CubeMX 进行中断配置
: s* v) ]8 |4 p0 \
CubeMX 中的 ADC 基本配置单通道采样相同,这里需要开启 ADC1 的中断,并且修改转换触发源,原来的软件触发改为使用定时器时间进行触发,TIM1 配置周期为 10ms,即每 10ms触发一次 ADC 转换。

, S9 W5 _1 B4 D0 h9 r8 }* n
相关操作函数说明:
HAL_StatusTypeDef HAL_ADC_Start_IT(ADC_HandleTypeDef *hadc)
功能:使能 ADC,以中断开启 ADC 规则组转换;
参数 1:ADC 句柄,根据实际需要填写;
返回:操作结果,HAL_OK,HAL_ERROR;
示例:HAL_ADC_Start_IT(&hadc1); //开启 ADC1 转换
注意:在 ADC 转换完成之后会触发中断,中断中读取采样数据

: x3 ^$ c% {0 O
HAL_StatusTypeDef HAL_ADC_Stop_IT(ADC_HandleTypeDef *hadc)
功能:关闭 ADC,停止规则组转换,关闭转换结束中断;
参数 1:ADC 句柄,根据实际需要填写;
返回:操作结果,HAL_OK,HAL_ERRORT;
. m/ b0 z% U6 F3 ?! K  b) i4 F
核心代码:
HAL_ADCEx_Calibration_Start(&hadc1,ADC_SINGLE_ENDED); //矫正
HAL_ADC_Start_IT(&hadc1);//中断方式启动 ADC
HAL_TIM_Base_Start(&htim1);//启动 TIM1

; P+ ?) t* C7 j5 ]( [
以上为 main 函数中外设初始化结束后的部分,首先对 ADC 进行校准,然后中断方式开启ADC 转换,这里主要是要开启 ADC 并且使能中断,然后开启 TIM1,通过 TIM 触发 ADC进行转换。

* T  N, N- D) v" p  I
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); //通过串口发送
}
}

% f& |4 M) [( z/ L% X7 ?
以上为 ADC 转换完成中断回调函数,该函数为 ADC 共用的,进入此函数首先要判断是哪个 ADC 转换完成了,然后读取相应 ADC 的数据寄存器,转换为浮点数电压,通过串口发送到上位机。
# ]6 j7 W7 y4 O4 J7 ]! Q5 q1 u7 c
实验现象:
下载烧录后可以观察到上位机串口助手打印 ADC 测量数据。
6.png
, k, }. \5 p' d1 W- \

3 E  O% {' Z; a3 S6 E9 _) k1 s
▲ 实验现象
九、DAC实验
+ Z0 k+ W$ M# C: Q7 o' u, I
实验目的:掌握和熟悉 DAC 单路输出的软件触发和定时器触发配置方法,配合 DMA 输出波形。

: {' U' l; ]5 `' \
1、DAC 软件触发输出实验

0 ~8 p. z- ^7 z3 K* `" G
CubeMX 配置如下,保存后生成对应的配置代码:
, s0 ]: M4 ^( |2 l% B$ c8 }
微信图片_20241122152645.png
- O; H1 s! ]% c; V* f

. p- m, D, x' _* L* \9 P5 M  |$ i
▲ CubeMX 进行 DAC 输出配置

$ z2 a0 r* w: j/ b+ h
本实验进行软件触发 DAC 输出,开启 DAC1 的 OUT1 输出,使用外部输出引脚,使用普通模式,并且使能输出缓冲,将触发设置为软件触发。

* _7 s$ @; \) @+ ]6 i- U: Y  R8 \
相关操作函数说明:
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 输出,如果想要修改生效,还需要使用下面的函数
$ [- d! |+ d6 U- T$ b! \4 a
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);// 改变输出值5 B' L  H7 D; V! z! M( @

" E: ?6 I# t2 s! k
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;

" A8 c$ W0 a8 O3 d& P
核心代码:
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);// 延时一毫秒
}
}
9 {5 _' W) I( \6 \
以上为 main 函数中外设初始化结束后的部分,主循环中根据正弦表切换 DAC 电压输出,1ms 进行一次切换,正选表一共 60 个点。
! ~' |) f* }5 V4 ^) `, z2 ~1 O" s+ C
实验现象:下载烧录后可以观察到 PA4 输出一个正弦波,频率约为 8.333Hz。

5 j% b7 \& R- r
微信图片_20241122165909.png 5 s" Z# H7 r& H8 S4 D+ N/ q
7 y+ J: v0 i! O$ r; c5 ]! n6 U
▲ 实验现象
( e2 d. {5 G' Y5 V, \  U
2、定时器触发 DMA 传输 DAC 输出实验

* d6 u, c2 Z& f1 E0 }
CubeMX 配置如下,保存后生成对应的配置代码:
微信图片_20241122152650.png
  C- p+ O0 e( g+ \8 b7 ?

2 v3 C. P0 @' T, I' H7 t. O- |
▲ CubeMX 进行 DAC 基本配置
微信图片_20241122152652.png
7 V( D8 y0 V8 J* G
* _2 |/ B1 a; i3 m# F! j
▲ CubeMX 进行 DMA 配置
微信图片_20241122152654.png ( U8 X1 S1 B5 G: r4 ^2 d

5 L/ J3 l$ d. J' m2 t
▲ CubeMX 进行 TIM4 配置
6 v, R5 A- t+ B/ Q5 R
本实验进行定时器触发 DAC 输出,开启 DAC1 的 OUT1 输出,使用外部输出引脚,使用普通模式,并且使能输出缓冲,将触发设置为 TIM4 触发,配置 DMA,使用循环模式,整字传输,配置 TIM4,设置定时器周期为 1ms。

& B- j% s- c! h  N1 F8 a
相关操作函数说明:
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 方式设置输出值

' n& p' C/ ~: o: I4 c- s
核心代码:
//正弦表
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 _. E0 s: `) f' v) j2 t8 v0 `4 b" @
以上为正弦表定义。
6 p4 B% O7 _1 k1 k: p: I6 W& 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();
}
/ b2 g, p- ~! x3 Y0 `7 q
以上为 main 函数中外设初始化结束后的部分,开启 TIM4 进行触发,以 DMA 方式开启DAC 转换输出。

6 w* d" v3 K% @4 S/ d: ~
实验现象:
下载烧录后可以观察到 PA4 输出一个正弦波,频率约为 12.5Hz。
微信图片_20241122152657.png
9 `4 {2 g6 U1 f4 d9 O, \6 a# W- ]
6 O. y2 W* z; d. Y, n1 q1 j
▲ 实验现象
1 [0 W9 K3 u0 D9 o. t' Q
3、定时器触发 DAC 输出噪声实验

3 w9 P3 q0 P" L
CubeMX 配置如下,保存后生成对应的配置代码:
微信图片_20241122152659.png
$ \/ |" i: n+ D4 N1 X" N, ~
▲ CubeMX 进行 DAC 输出配置
微信图片_20241122152701.png
" ?9 }0 h4 A2 z' O7 h6 O. `: a/ d1 |
! [1 @2 K2 \( L1 C6 J; O6 A4 P
▲ CubeMX 进行 TIM2 配置
; f: s# S& D4 t- \
本实验使用 TIM2 触发 DAC 进行输出,输出内容由 DAC 随机生成,产生噪声。
2 s4 G0 F5 r1 z- ~7 }
核心代码:
HAL_DAC_Start(&hdac1,DAC_CHANNEL_1);//启动 DAC 输出
HAL_TIM_Base_Start(&htim2);//启动 TIM2 触发 DAC
' T9 J0 I# Y$ a! c" G
以上为 main 函数中外设初始化结束后的部分,只需要开启 DAC 输出和定时器即可。

/ f+ e. }, j% H# M3 S
实验现象:
下载烧录后可以观察到 PA4 输出随机噪声。
微信图片_20241122152703.png
, t9 N" D- L3 n8 D
▲ 实验现象

% W/ F% p$ R  F! n; O+ ^5 a& j5 `7 h
如有侵权请联系删除
* s# c& F- r8 D2 K. C
转载自:AI电堂
4 T1 f* N0 \$ g- x% \! O
& d& `* w3 q6 M
赞 收藏 评论0 发布时间:2024-11-22 15:27

举报

0个回答

所属标签

相似技术帖

官网相关资源

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