|
第十、COMP 实验% k# |$ e3 |4 [! k! ^1 G 实验目的:掌握和熟悉 G474 内部的模拟比较器用法,包括触发方式以及与内部 DAC 级联使用等。3 t/ q" H, T H 1、软件读取 COMP 结果实验 CubeMX 配置如下,保存后生成对应的配置代码:1 h% V0 s5 R4 |3 a6 P
▲ CubeMX 进行 COMP 配置. O9 P4 N ^6 a' @* m$ r3 y1 x 本实验使用软件读取 COMP 比较结果,不需要配置触发,为了使比较结果更加直观,开启外部比较结果输出。: s: F# c1 n7 d! V) e 相关操作函数说明:" v6 o, B5 E: g8 p% X' Q, M. c HAL_StatusTypeDef HAL_COMP_Start(COMP_HandleTypeDef *hcomp)3 N1 b* }/ T+ @/ M% U: c6 `. ] 功能:开启比较器; 参数 1:比较器句柄,根据需要填写;; f# u( @$ g1 d$ Z 返回:操作结果,HAL_OK 或 HAL_ERROR; 示例:HAL_COMP_Start(&hcomp3);// 开启 COMP3HAL_StatusTypeDef HAL_COMP_Stop(COMP_HandleTypeDef *hcomp)2 D7 z2 Y/ Z" R `( \ 功能:关闭比较器; 参数 1:比较器句柄,根据需要填写; 返回:操作结果,HAL_OK 或 HAL_ERROR;* ?; U2 \1 D. ? ^ uint32_t HAL_COMP_GetOutputLevel(const COMP_HandleTypeDef *hcomp)- ~& ? l& U$ ~% L" A) d) B- f9 q! ]4 P 功能:读取比较器输出电平;6 w# \. ^/ t6 ]+ a( M 参数 1:比较器句柄,根据需要填写; 返回:比较结果,COMP_OUTPUT_LEVEL_LOW 或 COMP_OUTPUT_LEVEL_HIGH; 示例:result = HAL_COMP_GetOutputLevel(&hcomp3); //软件读取比较结果 0 W. H8 M8 C: Y8 O9 ^. N : G, u: M$ O( A$ n$ S7 S. D 核心代码: if(HAL_COMP_Start(&hcomp3) != HAL_OK)0 @$ ^3 f) r% g$ `+ G$ U) R //开启比较器" G0 }! G# Z" a! J3 h9 m { Error_Handler(); } X% G0 ?5 e+ r2 ~8 O1 h+ o2 Y while (1)7 x9 O% g, B& U# G7 e7 o7 N9 p6 G { result = HAL_COMP_GetOutputLevel(&hcomp3); //软件读取比较结果) M( I4 j% x+ D8 b if(result == COMP_OUTPUT_LEVEL_HIGH)//比较结果为 1,即 INP 大于 INM {+ y J l% i1 Q E HAL_GPIO_TogglePin(GPIOD,GPIO_PIN_11);//翻转 LED2 } HAL_Delay(100);- p. o! l' G8 G8 T* m8 E }9 f+ |) N$ \6 w# ~ $ ?* ?! ^: X6 P7 _ 8 y5 r* _, _4 B% a& n 以上为 main 函数中外设初始化结束后的部分首先开启比较器,然后在主循环中每 100ms读取一次比较结果,如果结果为 1,即正相输入大于反相输入,则进行 LED2 翻转。 5 m. \* @4 A4 U% m' o8 O 实验现象:. i' ?0 E( L8 `( E- [( ~' c 下载烧录后可以观察到拨动拨盘电位器,或者遮蔽光敏电阻时可以改变比较结果,CMP 亮时,LED2 状态不变,CMP 灭时,LED2 闪烁。5 G: B! N2 A: W9 J1 j& L7 C! I 2、中断读取 COMP 结果实验 CubeMX 配置如下,保存后生成对应的配置代码:
▲ CubeMX 进行 COMP 配置 本实验使用中断读取 COMP 比较结果,在比较结果变化的上升沿和下降沿都触发中断,为了使比较结果更加直观,开启外部比较结果输出。 6 q7 N6 v8 B% p9 L 核心代码:9 y2 U+ r4 w7 a# [" \3 c5 j if(HAL_COMP_Start(&hcomp3) != HAL_OK)8 j. P8 U" Q2 N* F: _. N# e8 W //开启比较器 {$ v, x: ]( e0 f* A, ? Error_Handler(); }# h" l: O( ~( z* b & b, L* Y! S/ S+ I+ D' i 7 z$ W- M8 [" S5 L6 @$ M 以上为 main 函数中外设初始化结束后的部分,这里只需要开启比较器即可,使用函数与上例相同。 , }9 b/ n L4 v3 D2 C% Y+ C void HAL_COMP_TriggerCallback(COMP_HandleTypeDef* hcomp)- b% x4 ~- Q/ l3 g/ @ { N$ ]9 ]9 }% u if(hcomp->Instance==COMP3)5 b8 F$ I# {" _: i. e7 G i4 q { uint8_t temp; temp = HAL_COMP_GetOutputLevel(&hcomp3);//读取比较结果' n$ i- n6 z" a5 m if(temp == COMP_OUTPUT_LEVEL_HIGH)//结果为 1,上升沿触发! T5 |. H- e! n { HAL_GPIO_WritePin(GPIOD,GPIO_PIN_10,GPIO_PIN_RESET);//点亮1 w* \& X& I. G LED1" p' m9 q( _: @7 z5 v% ~' w5 G HAL_GPIO_WritePin(GPIOD,GPIO_PIN_11,GPIO_PIN_SET);//熄灭 LED26 A7 b+ j3 M: m4 [ }* Y3 D4 t# N5 ~, i% h else7 G/ X- I) D) g4 ^. L. T* W { HAL_GPIO_WritePin(GPIOD,GPIO_PIN_10,GPIO_PIN_SET);//熄灭 LED1 HAL_GPIO_WritePin(GPIOD,GPIO_PIN_11,GPIO_PIN_RESET);//点亮 LED27 R! ^! n0 a0 B8 k, s+ @8 D } }, R1 M' n* B( f. t- p: x }/ s/ \# E+ R$ T' M% W {; a: H2 Y& `7 t 以上为 COMP 触发中断回调函数,该回调函数为比较器共用,需要判断中断源,如果开启了多个边沿,还需要判断具体边沿。实质上,COMP 中断触发与 EXTI 类似,实际就是将COMP 比较输出作为一个 IO 映射到 EXTI 进行中断。 9 q% | p/ ?' k( E( W 实验现象:4 A8 h/ |9 E$ V# V7 Q: ?& s 下载烧录后可以观察到拨动拨盘电位器,或者遮蔽光敏电阻时可以改变比较结果,CMP 亮时,LED1 灭,LED2 亮,CMP 灭时,LED1 亮,LED2 灭。% G# _! H, i8 _1 o3 m8 b . ^. p( j. e6 a & g$ J. k- h8 D2 v% Y, e 3、内部 DAC 级联比较实验 CubeMX 配置如下,保存后生成对应的配置代码:
▲ CubeMX 进行 COMP 配置" Z& Y' Q6 d3 n
& _( X: S m. X6 Q ▲ CubeMX 进行 DAC 配置9 i6 f# ^ T/ A 9 z+ O0 {' U& e n1 [ 本实验使用软件读取 COMP 比较结果,正相输入为光敏电阻,反相输入使用 DAC3 的 OUT1作为比较电压,为了使比较结果更加直观,开启外部比较结果输出。 + N7 B4 |, {' `! Z3 `; F 核心代码:$ D1 M. h' ~+ K1 @, x& `) ]; i void DAC3_CH1_Set_Vol(uint16_t vol)7 N0 n3 `$ O7 u" H! j {0 T+ o. Z( W- s1 |' @( Z( m double temp=vol; temp/=1000; temp=temp*4096/3.3;8 ^6 l% n! f! W5 _# b. i HAL_DAC_SetValue(&hdac3,DAC_CHANNEL_1,DAC_ALIGN_12B_R,temp);//12 位右对齐数据格式设置 DAC 值 }3 K' W9 L' |- y4 h % i& M+ W7 B& F+ U+ b 以上为 DAC 设置函数,通过该函数可以将输入的电压快速转换为 DAC 所需的寄存器数据。7 \5 F. U) `$ ?, p5 X 9 G3 u; k+ N" O3 r% P ; z1 j( |1 e$ k, @* g DAC3_CH1_Set_Vol(500);//DAC 输出设置为 500mv HAL_DAC_Start(&hdac3,DAC_CHANNEL_1);//生效 DAC 输出 if(HAL_COMP_Start(&hcomp3) != HAL_OK)4 E/ Q8 X7 H4 H //开启比较器 { Error_Handler();- J9 a, S0 w: n* l* @' P% H1 ~8 W } while (1) {& X; Y% B8 P8 w result = HAL_COMP_GetOutputLevel(&hcomp3); //软件读取比较结果" V) X6 n, ]5 _) p% ~/ t3 y' @9 t if(result == COMP_OUTPUT_LEVEL_HIGH)//比较结果为 1,即 INP 大于 INM; V; U( k+ G% Y! t$ E/ p0 e4 m' k2 S { HAL_GPIO_TogglePin(GPIOD,GPIO_PIN_11);//翻转 LED2 }# E S( d$ P/ z* X7 ^ HAL_Delay(100);- B/ ~2 g2 C* |3 F# p( s } 以上为 main 函数中外设初始化结束后的部分,首先设置好 DAC 输出电压,开启 DAC,然后开启比较器,最后在主循环中每 100ms 读取一次比较结果,如果结果为 1,即正相输入大于反相输入,则进行 LED2 翻转。% e4 g$ r7 N; A6 ]; L * T0 s- j( a- J V0 x 实验现象: 下载烧录后可以观察到遮蔽光敏电阻时可以改变比较结果,CMP 亮时,LED2 状态不变,CMP 灭时,LED2 闪烁。" M5 M) a8 e: n3 t. A: G 5 q" q( ` N0 B, [$ W 7 c6 `9 g8 B2 U 第十一、FLASH 实验 实验目的:掌握和熟悉 G474 内部的 FLASH 用法,包括 FLASH 读写应用等。FLASH 读取无需进行配置。 * P8 N/ w0 k, |! b$ m 核心代码: /* 函数名:FLASH_Write# O$ R Q) \* s * 描述 :flash 写入数据) Y9 `6 D( B/ h( ]9 _: q/ S% k7 f * 输入 :data 写入数据地址 addr flash 地址 size 写入字节数0 u, v$ c y: L Q Y" e * 输出 :无4 p) a% s3 l0 M& b * 调用 :FLASH_Write(data,FLASH_PAGE_127,8);//8 字节# z& T9 B# D' d! i9 C" M: y */! E; i" Q( H* h+ }9 @1 ? uint8_t FLASH_Write(uint8_t* data,uint32_t addr,uint16_t SIZE) {3 G7 ^3 I; m- [/ I/ @+ a FLASH_EraseInitTypeDef EraseInitStruct;//定义擦写操作结构体 uint32_t SECTORError = 0; uint64_t write_buff[10];//写入缓冲数组 uint8_t i = 0;+ _0 W3 T3 s2 W, V& E' s; s8 ` uint8_t size = SIZE/8;//8 字节写入次数! b1 {- h6 v4 o F6 ]8 h' G memcpy(write_buff,data,SIZE);//将输入的数组移动到写入缓冲区 HAL_FLASH_Unlock();//解锁4 @/ `; f1 S) A( X( e4 G P EraseInitStruct.Banks = FLASH_BANK_1; //存储区 1& @2 k6 N- `& S6 l! i4 w" `; y EraseInitStruct.TypeErase = FLASH_TYPEERASE_PAGES;//页擦除# Q& B: v8 U( Z8 D+ U4 V EraseInitStruct.Page = (addr-0x08000000)/FLASH_PAGE_SIZE;//擦除页, ], ^+ n+ @5 D- O4 e' m EraseInitStruct.NbPages = 1;//擦除页数# s- u! M1 H+ E* J if(HAL_FLASHEx_Erase(&EraseInitStruct,&SECTORError) != HAL_OK) //擦除页+ S, a8 |, a) o1 C { return 1;//擦除失败 ?" T8 d# |. B9 | }, `( a9 x. _4 C while(size) {9 @/ S% T5 Z% ^% u3 h) [: G* b, u7 { if(HAL_FLASH_Program(FLASH_TYPEPROGRAM_DOUBLEWORD,addr,write_buff)!= HAL_OK)//双字写入; |1 F, S8 L7 ^6 {& i {$ h. l2 w) F- Y. I return 2;//写入失败 } addr = addr+8;2 l, x0 B$ e! \* R6 H i++;1 G4 g; \7 d) C: W, }) V9 R* o6 g size--; }4 r/ z" b( L1 \8 }/ Y9 M HAL_FLASH_Lock(); //锁定 FLASH) r" Q5 \5 J. x/ V return 0; } /* 函数名:FLASH_Read$ d: z( u6 ?3 B( \ * 描述 :flash 读取数据 * 输入 :data 读取数据地址 addr flash 地址 size 读取字节数 * 输出 :无, I$ v9 r3 k3 e2 ^: X * 调用 :FLASH_Read(data,FLASH_PAGE_127,8);//8 字节 */( ^/ O$ J- n7 g0 B uint8_t FLASH_Read(uint8_t* data,uint32_t addr,uint16_t SIZE)+ U: v T4 e: F {6 m' N! X; F/ Y. y+ ^# E uint32_t read_buff[10]; //接收缓冲数组" k" |! V7 [8 b uint8_t i = 0;1 R* E8 J: Y& X/ q7 ~: ] uint16_t size = SIZE/4; //接收次数7 j/ U+ I3 @! v+ p while(size)7 ^4 R- X& J: p% P, L, {' B {0 Z0 u& p& {+ y8 J. l read_buff = *(__IO uint32_t*)addr; //从 FLASH 读取数据到接收缓冲数组 addr = addr+4;% B- D8 [" _1 A6 r' S7 Q" L! l i++; size--; } memcpy(data,read_buff,SIZE); //将数据从接收缓冲数组转移到接收数组( q8 O( n; p8 b return 0; }9 U9 j% e4 D! e# [; }+ e/ g $ |2 o4 {4 h4 {7 X 主要代码:8 `! T4 K! {, o5 J. Y7 p& M uint8_t data[8]={0,0,0,0,0,0,0,0}; char buf[30]="";: b' V6 E5 y0 V- b9 [ FLASH_Read(data,FLASH_PAGE_127,8);//8 字节2 G% i7 ~: n9 E data[0]++;FLASH_Write(data,FLASH_PAGE_127,8);//8 字节 snprintf(buf,10,"times:%d",data[0]); LCD_PutString(10,30,buf,Red,White,0);' B$ }# Q4 c+ M uint32_t UID1=READ_REG(*((uint32_t *)UID_BASE)); uint32_t UID2=READ_REG(*((uint32_t *)(UID_BASE+4U)));9 K4 C; A. s; j! f uint32_t UID3=READ_REG(*((uint32_t *)(UID_BASE+8U))); snprintf(buf,30,"UID:%0x-%0x-%0x",UID1,UID2,UID3); LCD_PutString(10,60,buf,Red,White,0);% k2 h% e0 Y) U1 k! {9 w3 _' y 实验现象: 下载烧录后可以观察到 LCD 显示 FLASH 测试次数以及芯片的 ID。% l- V/ S4 y0 G( j' N: B$ a1 ~- O
▲ 实验现象 - g$ n N& O' n7 K 2 a. A3 q7 E. q. o7 n6 ~ 第十二、单总线实验 实验目的:掌握和熟悉常用的单总线通信,包括 DS18B20,DHT11 读写应用。 CubeMX 配置如下,保存后生成对应的配置代码:. I4 {" d4 f! ^8 v
Q4 B( L' V% X& A ▲ 图 3.12.1 CubeMX 进行 GPIO 输出配置; z( w' t2 O3 ^0 P 7 r' e7 ~8 W7 l# C$ v 9 |" f: G% C2 N7 h S$ k4 Q 本实验进行单总线读取 DS18B20 和 DHT11。使用 CUBEMX 配置 IO 为输出模式。0 W& k5 h% R1 ^( _6 E$ o6 \ ) ^' j! w' [# d3 D1 v' s 读取 DS18B20 核心代码: /* 函数名:DS18B20_IO_OUT * 功能:初始化 DS18B20 的 GPIO 为输出模式$ q% J$ O) k/ B! q" g * 输入:无 * 输出:无 * 备注:无 */ void DS18B20_IO_OUT(void ); i" D0 R9 m$ I6 `& \ { GPIO_InitTypeDef GPIO_InitStruct = {0}; GPIO_InitStruct.Pin = GPIO_PIN_9; GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;: W N0 e7 w- A- E- e8 q$ Q GPIO_InitStruct.Pull = GPIO_NOPULL;9 `5 q) a: h" Z& t GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;+ c6 F: o- w! c+ G" \3 m; a+ Y* s HAL_GPIO_Init(GPIOD, &GPIO_InitStruct);8 i. \; x& m2 G8 p7 B4 p, C7 b0 L; X& ? } /* 函数名:DS18B20_IO_OUT1 z& z& g: L6 i* j0 p1 c; P! P1 M * 功能:初始化 DS18B20 的 GPIO 为输入模式8 J2 S& X7 v: G7 O" ~: L- f6 M * 输入:无 * 输出:无* G9 ?, n9 N |4 q0 e * 备注:无3 o; m9 c {: c/ H" B* [ */void DS18B20_IO_IN(void )1 ?" C' j+ C5 G1 D {/ M2 {; |* y' a% C( ?; I% Z- ^ GPIO_InitTypeDef GPIO_InitStruct = {0}; GPIO_InitStruct.Pin = GPIO_PIN_9;6 C+ X* [: e, P1 X- m& Z6 U2 n GPIO_InitStruct.Mode = GPIO_MODE_INPUT; GPIO_InitStruct.Pull = GPIO_NOPULL;* r5 _7 Q, R+ V. u( x# B; s5 l& o GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW; HAL_GPIO_Init(GPIOD, &GPIO_InitStruct);# @! w9 ?( v$ |3 @7 v6 F7 ^ } /* 函数名:DS18B20_Rst3 a9 z9 X+ Z3 U. r. a * 功能:复位 DS18B20 * 输入:无 * 输出:无* w) g" f1 w3 ?3 S8 O: t" k * 备注:无 */9 |( x- Y" j& d7 u7 t3 @ void DS18B20_Rst(void)( ~5 R7 S' q- B1 o: i a {3 |6 Q1 U' y+ N5 S7 J4 Z DS18B20_IO_OUT(); //SET PG11 OUTPUT# S+ A6 W* W& u; q' x4 {$ ], v DQ_OUT_LOW(); //拉低 DQ1 b0 _' m3 W& \. r) L delay_us(750); o3 P' G C& {+ X% v5 |! }6 L //拉低 750us DQ_OUT_HIGH(); //DQ=1 delay_us(15);; b8 x" B. I/ Q7 C. ]- U V //15US8 u% Y# Q. r) Z) b* F L' k }$ H8 J) @) T, a$ N /* 函数名:DS18B20_Check * 功能:等待 DS18B20 的回应 * 输入:无& R" }3 M' |& q/ O( r$ N7 g+ ` * 输出:返回 1:未检测到 DS18B20 的存在 返回 0:存在% O2 c. h( P# P! U9 s3 j6 d7 ~ * 备注:无 */, g# `: s6 W+ l9 Y uint8_t DS18B20_Check(void) { uint8_t retry=0; DS18B20_IO_IN(); //SET PG11 INPUT2 a g6 d1 q1 u+ z. N5 i; d- q while (DQ_GET_IN()&&retry<200); B4 F+ Y: {) w% I P1 \ {8 N) p' y$ C" F6 j! b. G% _- t k retry++;delay_us(1); }; if(retry>=200)return 1; else retry=0; while (!DQ_GET_IN()&&retry<240) {0 j# {4 }6 O& k; o; ~ retry++; delay_us(1);- v6 g# w% J9 Z% y/ h. K- N };% W5 @2 c7 R) Z# |1 |+ T if(retry>=240)return 1; return 0; @! v; y& s& ?4 q9 K. i" f }9 A- [! z. I) m6 J1 W) Z /* 函数名:DS18B20_Read_Bit * 功能:从 DS18B20 读取一个位 * 输入:无1 `; @2 N0 Z* W * 输出:返回值:1/0$ U( z' [8 A; M * 备注:无8 l! f3 |$ f3 m4 I/ d */9 r p7 J$ o; z) i& l# A uint8_t DS18B20_Read_Bit(void) { uint8_t data;& E e) N: @% L4 @9 Y0 _ DS18B20_IO_OUT(); //SET PG11 OUTPUT1 G9 i3 K+ R; [$ M DQ_OUT_LOW();delay_us(2); DQ_OUT_HIGH(); DS18B20_IO_IN(); //SET PG11 INPUT" ]" _/ W4 Y* l; J3 [. G delay_us(12);/ C" K5 ^+ {: l+ ]- c6 c if(DQ_GET_IN())data=1;% n! P' n1 A `6 r' S- ?1 ^* q+ G else data=0; delay_us(50); return data; }2 l. C8 I- P$ A0 \( R /* 函数名:DS18B20_Read_Byte' t7 B/ b% Y9 M, _/ H * 功能:从 DS18B20 读取一个字节+ i) |# L, e$ j' i( l* Q3 a * 输入:无 * 输出:返回值:读到的数据; ^. g0 A2 m0 K& | B# ]3 i * 备注:无8 J# O, \0 w0 E# _. |: m3 L */) \* q" H# e4 i* b5 @ uint8_t DS18B20_Read_Byte(void) {' I/ r( T3 S6 _: ^6 O uint8_t i,j,dat;: z3 m5 e5 q }7 k dat=0; for (i=1;i<=8;i++) {. G/ v% _8 O( m: u `# x+ z" K j=DS18B20_Read_Bit();' c* I; G% S$ I. P5 ^ dat=(j<<7)|(dat>>1);) N6 E! ]- Q; A' N7 G } return dat;: g1 Y3 Z% D- g: E& g3 q } /* 函数名:DS18B20_Write_Byte& W u7 E+ _& x+ J * 功能:写一个字节到 DS18B20$ q+ c; d y- D, l * 输入:dat:要写入的字节 * 输出:无9 C& m3 M$ F0 i$ ] * 备注:无% K1 b, a5 q- f3 a+ A2 I */% F# h. L2 D X7 V void DS18B20_Write_Byte(uint8_t dat)7 ?& j% }1 o& q/ j {3 j' [2 H- V+ I* o, X I, l( W uint8_t j; uint8_t testb;9 x3 r ~6 `/ J DS18B20_IO_OUT(); //SET PG11 OUTPUT;# p' N# s k6 f+ e: o for (j=1;j<=8;j++) { C+ l; Z, ~( U+ O' O testb=dat&0x01;# |$ ?( m) X1 v* @; j dat=dat>>1; if (testb)) n% U% L5 z! F) T& ?, l1 K {- u' Q5 \% E" p; b8 `- e DQ_OUT_LOW(); // Write 1; r' { |1 W3 {( X delay_us(2);5 U9 _' V O0 I7 E t DQ_OUT_HIGH(); delay_us(60); }2 z3 D& C- r2 x p else; O% \: ?$ C: @$ @; ?/ C5 L { DQ_OUT_LOW(); // Write 08 q+ o3 a g9 r+ v) c delay_us(60); DQ_OUT_HIGH();. H2 t* i" z/ p delay_us(2); }- K$ A' `0 J* V' z } }) [9 [7 A- h9 F: e9 c /* 函数名:DS18B20_Start * 功能:开始温度转换 * 输入:无 * 输出:无, R: H* m' P* v Q! ? F4 x8 j& d * 备注:无$ h$ b8 Y$ t2 _ b& @" [: ] */' v0 X! J! `1 d- x( _9 j" K // void DS18B20_Start(void) { DS18B20_Rst();1 c3 B6 i. T0 T/ m) b2 N DS18B20_Check();) H7 f3 S0 G9 N3 b: Y DS18B20_Write_Byte(0xcc);// skip rom DS18B20_Write_Byte(0x44);// convert, p" a) V1 z, t4 g' w2 @ }4 {3 g0 e9 A' W /* 函数名:DS18B20_Start5 z, K' W- ~- B. m" v * 功能:从 ds18b20 得到温度值 精度:0.1C( P+ K9 k! g8 v) p# R" D * 输入:无 * 输出:返回值:温度值 (-55.0~125.0) * 备注:无 */ short DS18B20_Get_Temp(void) { uint8_t temp;) }, a) V! Y' E1 }4 @8 | uint8_t TL,TH; short tem;DS18B20_Start ();( K$ U9 X- |) i // ds1820 start convertDS18B20_Rst();1 D3 W: n, L' W! P1 a4 D DS18B20_Check(); DS18B20_Write_Byte(0xcc);// skip rom3 }/ Y7 ?6 i8 c* T( Q* ?% b1 k DS18B20_Write_Byte(0xbe);// convert: q) _6 h4 W& k0 a! a" G" }: ~ TL=DS18B20_Read_Byte(); // LSB8 t$ h/ \0 h0 D TH=DS18B20_Read_Byte(); // MSB2 M6 U3 i3 k6 C4 ^7 V4 `5 d if(TH>7) { TH=~TH; TL=~TL;' L- \; h+ r- z0 U temp=0; //温度为负: o0 s. U. x% D; i }else temp=1; K$ I) F/ S1 C- b& ?) D9 N //温度为正 tem=TH; //获得高八位2 i: n5 d6 S( ~6 T tem<<=8;tem+=TL; //获得底八位" y; X* e! x) f tem=(float)tem*0.625; //转换4 i2 W+ S' a& w+ U$ U char buf[20]="";: z, `* _4 V3 |7 F5 C if(temp)snprintf(buf,10,"Temp:%.1f",tem/10.0);! K" Q5 D2 K2 o# v: x2 ] else snprintf(buf,10,"Temp:%.1f",tem/10.0); LCD_PutString(10, 10, buf, White,Blue,1); if(temp)return tem; //返回温度值 else return -tem; } 读取 DHT11 核心代码:( m4 |( R/ C0 U" F- o+ l7 @ /* 函数名:delay_us * 功能:微秒延时. l, K2 z1 u5 t) n0 M * 输入:delay 延时多少微秒, a) Z$ F! E3 f * 输出:无 * 备注:无4 K( [9 y2 N5 C* T* Q; u */! Q- ?0 F- L# w) P5 \ #define CPU_FREQUENCY_MHZ 170 // STM32 时钟主频 void delay_us(__IO uint32_t delay)2 W% h5 ?, i) v$ s5 B) s {& w. ~! L) O' \: b$ Q& v int last, curr, val; int temp; while (delay != 0) { temp = delay > 900 ? 900 : delay;. Y) \) D& F6 m n& r last = SysTick->VAL;4 | ~' n8 `7 G9 \ curr = last - CPU_FREQUENCY_MHZ * temp;: ?6 T5 N, l! b( ~6 v+ l& x9 Z U+ o if (curr >= 0) {2 R3 ?; p: a; [6 K do; f* n2 l [! J) ]' B0 R: r {3 x* n) w) \9 U9 g: z' T val = SysTick->VAL;; }* N8 Q/ X4 I9 t) |# n. k } while ((val < last) && (val >= curr)); }6 a! x% o. }4 Y% [ else' S. L8 J7 ^; M+ {: T3 `0 H/ o {# l: b& d4 C: |4 x" Q curr += CPU_FREQUENCY_MHZ * 1000; do {4 ~2 q* v3 {2 a val = SysTick->VAL; } while ((val <= last) || (val > curr));! |$ E7 G% d& r }" P8 G/ a8 x# q1 T% ^) m delay -= temp;9 y8 O- E2 b. U. Z } } unsigned int rec_data[4]; /* 函数名:DH11_GPIO_Init_OUT * 功能:初始化 DHT11 的 GPIO 为输出模式0 r- E( J: _( Z F# U& _2 m * 输入:无 * 输出:无 * 备注:无* D, B% n& ~1 Y. V, g* b% U8 |) N */ void DH11_GPIO_Init_OUT(void) { GPIO_InitTypeDef GPIO_InitStruct = {0}; GPIO_InitStruct.Pin = GPIO_PIN_9; GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP; GPIO_InitStruct.Pull = GPIO_NOPULL; GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;" u8 k1 R* ]) O5 b/ x HAL_GPIO_Init(GPIOD, &GPIO_InitStruct); }: H6 }# t5 ^! k $ y7 ^2 E% [1 q# A4 N) }( U // 对于 M0 来说,是输入 /* 函数名:DH11_GPIO_Init_IN * 功能:初始化 DHT11 的 GPIO 为输入模式 * 输入:无 * 输出:无2 b5 n' ^* `6 f * 备注:无6 u# F( [# F+ M2 E */ void DH11_GPIO_Init_IN(void)3 @/ P% z* g a( W ]: b) v {$ S% P- |& s* e5 ]9 p( r' e GPIO_InitTypeDef GPIO_InitStruct = {0}; GPIO_InitStruct.Pin = GPIO_PIN_9;+ M! n& r/ r. Y, d( Y" C GPIO_InitStruct.Mode = GPIO_MODE_INPUT;1 @& s: z$ C! E8 j* S$ q9 P# K6 z GPIO_InitStruct.Pull = GPIO_NOPULL;+ b0 F% v( G7 p' P3 r+ z" L GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;& H4 _# L. b* J6 d HAL_GPIO_Init(GPIOD, &GPIO_InitStruct);( A9 J( D! Z& x. D1 T; Z // DL_GPIO_initDigitalInput(GPIO_DQ_dht11_and_ds18b20_IOMUX);//配置为上拉输入) }" L6 r: ]7 `7 e, o* } }6 U6 r$ L! f( q% f% X7 o // 主机发送开始信号 /* 函数名:DHT11_Start * 功能:主机发送开始信号$ P! j0 l9 v: C' k * 输入:无0 |' T1 o9 @, G( l * 输出:无1 e" G0 C3 f, L: `# e * 备注:无 */ void DHT11_Start(void)1 v/ d1 }. F" I6 O; \8 y: E {- g% j# S2 X: X* Z8 c DH11_GPIO_Init_OUT(); // 输出模式2 R) ?! x+ R: M6 K: D6 l! f9 A5 y dht11_high; // 先拉高 delay_us(30); dht11_low; // 拉低电平至少 18ms) |; C6 ], |8 U- w+ [0 h6 h3 ?# z HAL_Delay(20); dht11_high; // 拉高电平 20~40us3 E+ e5 _% \9 ]$ F6 k/ S DH11_GPIO_Init_IN(); // 输入模式2 ^2 n: r* L Z; J' A; l% z8 Q delay_us(30); }/ S1 K* o" L" H8 b // 获取一个字节 /* 函数名:DHT11_Rec_Byte2 V' W' Z. D3 s6 W, Q" ^+ T$ M * 功能:获取一个字节3 n$ I& C( ` i* [" l * 输入:无7 `) I4 L- S( [# Z! } * 输出:读取的字符& Q- K5 {* V9 C) U * 备注:无& n, B% S7 v1 e/ q1 X. B */ char DHT11_Rec_Byte(void) {. K$ K& S8 ^/ k/ P v unsigned char i = 0;1 j6 T6 l7 y. I3 r! e, |2 w8 ]) M; V' t unsigned char data;* }% i, D! `& g0 ~5 [4 ^* T for (i = 0; i < 8; i++) // 1 个数据就是 1 个字节 byte,1 个字节 byte 有 8 位 bit$ S, i# i# M+ W5 {, Z+ r { while (dht11_read == 0)8 m3 x9 f0 u% h: f0 j ;- Z/ X9 l( m8 I9 e! ]# H. [ // 从 1bit 开始,低电平变高电平,等待低电平结束 delay_us(30); // 延迟 30us 是为了区别数据 0 和数据 1,0 只有 26~28us2 a- G" a9 P# |4 {2 M; t4 ] data <<= 1; // 左移 if (dht11_read == GPIO_PIN_SET) // 如果过了 30us 还是高电平的话就是数据 1 { data |= 1; // 数据+1! ]3 Q. b8 q0 ]+ ^ f- j; g% B# x }) v, P m8 E! R K- u1 \ while (dht11_read == GPIO_PIN_SET) ; // 高电平变低电平,等待高电平结束( R$ }. g$ t& d$ ^ } return data; } // 获取数据- o# u" n2 P: j /* 函数名:DHT11_REC_Data( L( F( Y8 \- o4 { * 功能:获取 DHT11 数据并打印+ ~0 M" W) O f1 \: E4 D1 J& w * 输入:无 * 输出:无 * 备注:无2 B) l/ }7 l2 k( L7 d1 ?+ ?- [ */ void DHT11_REC_Data(void) {! O1 g3 X3 L# d' a! |0 M) g unsigned int R_H, R_L, T_H, T_L; unsigned char RH, RL, TH, TL, CHECK;0 Z9 K6 E1 x4 f' G% P3 O* @1 b0 e& [ DHT11_Start(); // 主机发送信号 //dht11_high; // 拉高电平 if (dht11_read == 0) // 判断 DHT11 是否响应 {8 n" O( ] ?9 o6 ]1 g while (dht11_read == 0); // 低电平变高电平,等待低电平结束( N: s! `4 G! j$ N while (dht11_read == GPIO_PIN_SET); // 高电平变低电平,等待高电平结束 R_H = DHT11_Rec_Byte();5 L0 N9 R7 u% w/ ?' S$ |# m R_L = DHT11_Rec_Byte();5 ]* Y9 t6 e$ C/ D T_H = DHT11_Rec_Byte();/ B6 d$ z9 ]8 [& u T_L = DHT11_Rec_Byte(); CHECK = DHT11_Rec_Byte(); // 接收 5 个数据 //DH11_GPIO_Init_OUT(); //dht11_low; // 当最后一 bit 数据传送完毕后,DHT11 拉低总线 50us ~' M& B! Z. H3 g+ Q0 c2 t3 J% s delay_us(55); // 这里延时 55us0 x; y- P! W' @3 o" Q. e3 ? //dht11_high; // 随后总线由上拉电阻拉高进入空闲状态。" G+ A6 {* G% f. Z6 g2 A if (R_H + R_L + T_H + T_L == CHECK) // 和检验位对比,判断校验接收到的数据是否正确 { RH = R_H; RL = R_L;7 n8 U* x- t2 n% J3 g3 g. e TH = T_H; TL = T_L;% u4 b2 y, M$ i/ i9 O* y. K } } , m9 Y2 z0 [0 B2 r' E4 i/ ? rec_data[0] = RH; rec_data[1] = RL;$ |4 c& v$ g B3 S! Z6 ~1 N rec_data[2] = TH;! W/ j! a2 B) e' u rec_data[3] = TL;9 P! u) c! k4 H char buf[20]=""; snprintf(buf,10,"Temp:%d.%d",TH,TL);+ J' @( ~. D h# b LCD_PutString(10, 10, buf, White,Blue,1); snprintf(buf,10,"Hum:%d.%d",RH,RL);8 }7 J& O" u( t# ^' K. [: L& T LCD_PutString(10, 30, buf, White,Blue,1);}. C8 |2 O9 V0 x# |, u$ X) ^+ F7 H 主要代码: while(1){. K6 `4 Z' q. f9 P2 T DHT11_REC_Data();//DHT11 读取7 ~* {: t# }+ n c$ y7 A( p/ _ //DS18B20_Get_Temp();//18B20 读取2 L0 z# j$ V9 D9 b/ ] HAL_Delay(1000);: D6 f0 V) @4 o, k1 V+ m. E }) O; F; y! _! U9 j 0 m+ y0 ]7 U, a0 p9 r C: n. S 实验现象:1 k( Y; j( Q ^8 L3 i) N! p/ N; l 下载烧录后可以观察到 LCD 显示 DS18B20 测试的温度,或者 DHT11 测量的温度和湿度。
▲ 图 3.12.2 实验现象& \, z& W0 c8 B/ Q. l 第十三、独立看门狗) f1 L7 z; K6 Q6 t8 K7 h 实验目的:掌握和熟悉独立看门狗用法,包括喂狗操作等。8 m( r1 X0 I; C! [% _# W! \1 \ ; d% T- n: f/ X. k1 ` CubeMX 配置如下,保存后生成对应的配置代码:
▲ 图 3.13.1 CubeMX 进行独立看门狗配置' o9 B) ]) \ z: L7 _2 b) Q 本实验进行独立看门狗配置。使用 CUBEMX 配置 IO 为输出输入模式实现 LED 指示和按键读取,GPIO 配置参考上文。程序中使用按键喂狗,如果没在 1s 内喂狗,系统将会自动复位。 主要代码:: b/ Z6 o% [$ V. a: `; r- V( Z while (1); L3 B) G3 ]9 a) O { if(HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_4)==0) { HAL_Delay(5);* F' V' a, o1 N if(HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_4)==0) { HAL_IWDG_Refresh(&hiwdg);4 M- C [! G; t3 { HAL_GPIO_TogglePin(GPIOD,GPIO_PIN_10);- X G7 y1 x0 r% [0 b/ N while(!HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_4));. E8 b4 s8 N* _) b, K7 R1 w } } /* USER CODE END WHILE */ /* USER CODE BEGIN 3 */ }. w- h. ^: `0 t; f6 L1 r * f- p- k/ v% ?+ S, V0 Z6 v' n" A: t ' P! k9 }0 o* ^; _ 实验现象: 下载烧录后可以观察到如果在一定时间内进行喂狗操作,系统会复位,LCD 重新加载。
▲ 图 3.13.2 实验现象 q2 ^ ~/ |5 D8 M 转载自: AI电堂. B# a# d, o/ u 如有侵权请联系删除7 a9 i/ J+ d4 _7 S* t $ {* A6 ] h' _. B- M# m3 u " @5 k I: R, @( i2 V* b 2 p# G; ^8 V& t' p* `! L6 Y% Q: c |
STM32G47x 双 Bank 模式下在线升级
DMA 原理从 0 到 1:为什么它能解放 CPU?一文讲透 DMA 的工作流程
【经验分享】STM32G4之基本定时器
NUCLEO-G474RE 扩展 LSM6DSO 传感器数据融合实操演示全解析
经验分享 | STM32G474 高精度定时器同步功能全解析 从内部互联到多芯片协同的实现方案
经验分享 | STM32G4双BANK启动应用演示
STM32G4 LPTIM+DMAMUX 实现并行输出应用示例
实战经验 | LAT1578 SAU对NSC分区的影响
经验分享 | STM32G474 HRTIM Triggered-half模式实现两相交错电源180°相位同步方案
STM32大神笔记,超详细单片机学习汇总资料
微信公众号
手机版