
第十、COMP 实验 实验目的:掌握和熟悉 G474 内部的模拟比较器用法,包括触发方式以及与内部 DAC 级联使用等。 * y+ E" G* u6 g6 H8 \1 p 1、软件读取 COMP 结果实验5 R' t7 z5 m9 @; z6 Z CubeMX 配置如下,保存后生成对应的配置代码: ![]() 8 Y( ]3 k! M2 R, g3 p/ T# Y/ y ▲ CubeMX 进行 COMP 配置 本实验使用软件读取 COMP 比较结果,不需要配置触发,为了使比较结果更加直观,开启外部比较结果输出。 相关操作函数说明:, Y( }2 s4 O* s HAL_StatusTypeDef HAL_COMP_Start(COMP_HandleTypeDef *hcomp)' R2 f# x# A* |; j2 @ [/ u% U 功能:开启比较器;; X2 s: D# W% i 参数 1:比较器句柄,根据需要填写; 返回:操作结果,HAL_OK 或 HAL_ERROR; 示例:HAL_COMP_Start(&hcomp3);// 开启 COMP3HAL_StatusTypeDef HAL_COMP_Stop(COMP_HandleTypeDef *hcomp) 功能:关闭比较器;: t: K9 D$ p" v5 ]8 k& P 参数 1:比较器句柄,根据需要填写;- x4 y) M7 i* \; V8 Q 返回:操作结果,HAL_OK 或 HAL_ERROR; uint32_t HAL_COMP_GetOutputLevel(const COMP_HandleTypeDef *hcomp) 功能:读取比较器输出电平;$ }1 O t5 Q/ d' ] 参数 1:比较器句柄,根据需要填写; 返回:比较结果,COMP_OUTPUT_LEVEL_LOW 或 COMP_OUTPUT_LEVEL_HIGH;2 m8 z+ t5 m6 n2 _$ C 示例:result = HAL_COMP_GetOutputLevel(&hcomp3); //软件读取比较结果 s8 ~3 _9 O# f5 J+ ~2 i $ m$ Q" Z1 Z5 j 核心代码: if(HAL_COMP_Start(&hcomp3) != HAL_OK)+ ~7 H: D' s, l# z //开启比较器 {% z7 i- X6 W1 ? C Error_Handler(); } while (1) { result = HAL_COMP_GetOutputLevel(&hcomp3); //软件读取比较结果 if(result == COMP_OUTPUT_LEVEL_HIGH)//比较结果为 1,即 INP 大于 INM { HAL_GPIO_TogglePin(GPIOD,GPIO_PIN_11);//翻转 LED2 } HAL_Delay(100); } ; X8 m$ Y. Z! ]- w5 X# i6 L 以上为 main 函数中外设初始化结束后的部分首先开启比较器,然后在主循环中每 100ms读取一次比较结果,如果结果为 1,即正相输入大于反相输入,则进行 LED2 翻转。# Q/ w- A; l% F9 Y2 i5 v, U" z / P g; h- v5 S: x& \! j / N( c5 R3 @( F7 M6 V- U7 a 实验现象:( _, b, [5 N4 }* _$ G 下载烧录后可以观察到拨动拨盘电位器,或者遮蔽光敏电阻时可以改变比较结果,CMP 亮时,LED2 状态不变,CMP 灭时,LED2 闪烁。% W, s8 f3 @0 b6 g/ K $ ~& x2 Q1 M& k" n) h 2、中断读取 COMP 结果实验 CubeMX 配置如下,保存后生成对应的配置代码:( Q l. I- e, O. @0 ? ![]() ) i3 o6 g$ p) a ▲ CubeMX 进行 COMP 配置! q% f/ o9 i; U" V3 f) U* f) _ D : W* B I$ \* y6 O' T$ P 本实验使用中断读取 COMP 比较结果,在比较结果变化的上升沿和下降沿都触发中断,为了使比较结果更加直观,开启外部比较结果输出。7 _8 @5 a/ B, j, E- Y ) E5 W- z, b+ C7 [3 A& m; j2 p 核心代码:8 y/ q+ N) O* W if(HAL_COMP_Start(&hcomp3) != HAL_OK) //开启比较器 { Error_Handler();( |7 Q; h& s9 [- d" w9 U/ [ }0 e; w8 S7 w, |" t) l; Z 以上为 main 函数中外设初始化结束后的部分,这里只需要开启比较器即可,使用函数与上例相同。 ( N% `! q4 _- o9 p$ z( x void HAL_COMP_TriggerCallback(COMP_HandleTypeDef* hcomp) {) G- g% @1 u1 j& d* |9 ]. } if(hcomp->Instance==COMP3)- M7 {( @6 ~6 d {) {9 N8 }' D* b- \ uint8_t temp;, p( A4 d. p' }8 X6 F temp = HAL_COMP_GetOutputLevel(&hcomp3);//读取比较结果 if(temp == COMP_OUTPUT_LEVEL_HIGH)//结果为 1,上升沿触发' p$ M: ?% M& n" m- t" u( l7 O2 G {8 c; H$ B% J7 J HAL_GPIO_WritePin(GPIOD,GPIO_PIN_10,GPIO_PIN_RESET);//点亮 LED1, u# w7 w* [# U* P- Z3 {& N HAL_GPIO_WritePin(GPIOD,GPIO_PIN_11,GPIO_PIN_SET);//熄灭 . t: p g4 V6 h0 o+ n- Q' o* E9 { LED2 } _" D! ?) Q' @ else' n; z- j% ^2 M { HAL_GPIO_WritePin(GPIOD,GPIO_PIN_10,GPIO_PIN_SET);//熄灭 LED1, F. U2 z6 X% u. ~1 C3 ` HAL_GPIO_WritePin(GPIOD,GPIO_PIN_11,GPIO_PIN_RESET);//点亮; N. f) R7 v- j& q$ L# b% A LED2 }& S/ k- C" q* |) ~( T } } 2 O8 p% R: e$ J* ^3 c 以上为 COMP 触发中断回调函数,该回调函数为比较器共用,需要判断中断源,如果开启了多个边沿,还需要判断具体边沿。实质上,COMP 中断触发与 EXTI 类似,实际就是将COMP 比较输出作为一个 IO 映射到 EXTI 进行中断。 实验现象:7 J0 @5 n! u5 {$ y+ }* ? 下载烧录后可以观察到拨动拨盘电位器,或者遮蔽光敏电阻时可以改变比较结果,CMP 亮时,LED1 灭,LED2 亮,CMP 灭时,LED1 亮,LED2 灭。' Q. n q$ S' q+ N( {$ p ; z, h+ m: J, c5 r 3、内部 DAC 级联比较实验 CubeMX 配置如下,保存后生成对应的配置代码:* c6 p& G& c# g ![]() $ Q3 e% U2 T0 a& F ▲ CubeMX 进行 COMP 配置 ![]() ▲ CubeMX 进行 DAC 配置 ; ~& y4 a% M/ u 本实验使用软件读取 COMP 比较结果,正相输入为光敏电阻,反相输入使用 DAC3 的 OUT1作为比较电压,为了使比较结果更加直观,开启外部比较结果输出。" h& y' D& X# D3 i1 V% y5 y ) g9 e' d S" ^& v) h2 R" f 核心代码: void DAC3_CH1_Set_Vol(uint16_t vol)3 h" B6 W3 v$ V2 f( C4 k( F {) }4 e3 [) S" Z' ]. [0 @* y double temp=vol;0 z5 U, G" S9 m3 ^5 ]; P temp/=1000; temp=temp*4096/3.3; HAL_DAC_SetValue(&hdac3,DAC_CHANNEL_1,DAC_ALIGN_12B_R,temp);//12 位右对齐数据格式设置 DAC 值5 Y6 I7 M- a* @- m- R4 j0 z) l }" ^. L# S# B1 J# G, Y ( v4 q0 K+ h7 \, m ( O$ V4 \5 O5 Y! X- l' m5 {( m1 a& J 以上为 DAC 设置函数,通过该函数可以将输入的电压快速转换为 DAC 所需的寄存器数据。6 k& v8 z. g( h& g- I ( H; T6 A6 e! k DAC3_CH1_Set_Vol(500);//DAC 输出设置为 500mv HAL_DAC_Start(&hdac3,DAC_CHANNEL_1);//生效 DAC 输出2 X3 ?5 k0 a$ J2 {: b5 y6 D if(HAL_COMP_Start(&hcomp3) != HAL_OK)9 b5 i* ]* b, U* ]: B //开启比较器 { Error_Handler(); } while (1)) ~% {/ l) p5 z) ]7 c { result = HAL_COMP_GetOutputLevel(&hcomp3); //软件读取比较结果. I8 n6 W6 n1 b$ l O if(result == COMP_OUTPUT_LEVEL_HIGH)//比较结果为 1,即 INP 大于 INM { HAL_GPIO_TogglePin(GPIOD,GPIO_PIN_11);//翻转 LED2 } HAL_Delay(100); } 以上为 main 函数中外设初始化结束后的部分,首先设置好 DAC 输出电压,开启 DAC,然后开启比较器,最后在主循环中每 100ms 读取一次比较结果,如果结果为 1,即正相输入大于反相输入,则进行 LED2 翻转。' ?8 k6 l- s j$ u 实验现象: 下载烧录后可以观察到遮蔽光敏电阻时可以改变比较结果,CMP 亮时,LED2 状态不变,CMP 灭时,LED2 闪烁。& B/ J2 k7 P7 \1 o& z9 f% [. u 1 w" `" O! u- W7 o& G. X5 c- l 第十一、FLASH 实验 实验目的:掌握和熟悉 G474 内部的 FLASH 用法,包括 FLASH 读写应用等。FLASH 读取无需进行配置。 0 b F2 ]" g/ g- H! N+ f1 J 核心代码: /* 函数名:FLASH_Write: V2 b; b u; u6 J8 s. M0 i * 描述 :flash 写入数据! ]; n- \& P! e1 Q * 输入 :data 写入数据地址 addr flash 地址 size 写入字节数 * 输出 :无3 N% ?! o# F$ Q% W0 c; h * 调用 :FLASH_Write(data,FLASH_PAGE_127,8);//8 字节 */ uint8_t FLASH_Write(uint8_t* data,uint32_t addr,uint16_t SIZE) {5 s, b( `. V0 e' } FLASH_EraseInitTypeDef EraseInitStruct;//定义擦写操作结构体 uint32_t SECTORError = 0;" S( T, _! u( W/ ~+ L uint64_t write_buff[10];//写入缓冲数组 uint8_t i = 0; uint8_t size = SIZE/8;//8 字节写入次数 memcpy(write_buff,data,SIZE);//将输入的数组移动到写入缓冲区 HAL_FLASH_Unlock();//解锁 EraseInitStruct.Banks = FLASH_BANK_1; //存储区 1$ u1 r/ n9 i8 `/ n6 f' a) } EraseInitStruct.TypeErase = FLASH_TYPEERASE_PAGES;//页擦除; u/ }% P: g6 l1 u* ^# U6 x+ @: B EraseInitStruct.Page = (addr-0x08000000)/FLASH_PAGE_SIZE;//擦除页0 P/ Y: Y; T+ e EraseInitStruct.NbPages = 1;//擦除页数$ `, O2 K! a8 H/ C3 y5 X: N if(HAL_FLASHEx_Erase(&EraseInitStruct,&SECTORError) != HAL_OK) //擦除页) S/ M: A* `$ L1 W7 { { return 1;//擦除失败3 }% Q$ X/ \' d& H( v9 i3 v }$ }: B4 J6 {" A! q( v- v while(size) {4 L! v R5 E& u9 p0 u- V) n if(HAL_FLASH_Program(FLASH_TYPEPROGRAM_DOUBLEWORD,addr,write_buff)!= HAL_OK)//双字写入+ ?+ Z' ]5 K# _1 D2 y {3 C l. M1 U. U5 E& g7 B return 2;//写入失败 } addr = addr+8;1 U l Y; g) p* O i++; size--;* b% D& f. z3 V } HAL_FLASH_Lock(); //锁定 FLASH5 Q2 g' T% r# x6 `6 c return 0;4 h1 W0 Y- l, e9 x8 `! T }* h" \' e/ S0 S" [/ I B /* 函数名:FLASH_Read * 描述 :flash 读取数据6 t9 C& o- Z2 r- y * 输入 :data 读取数据地址 addr flash 地址 size 读取字节数6 r/ @+ Y8 c! W( I * 输出 :无 * 调用 :FLASH_Read(data,FLASH_PAGE_127,8);//8 字节( R8 g0 }' a$ N* x7 s" L; M1 c2 a! V */- j4 ^& {- M+ \% ?$ j2 B1 Y uint8_t FLASH_Read(uint8_t* data,uint32_t addr,uint16_t SIZE) {- F: _4 E7 z& j1 q$ B uint32_t read_buff[10]; //接收缓冲数组 uint8_t i = 0;; d9 d, Q7 s# Q M! o uint16_t size = SIZE/4; //接收次数2 m5 j5 U! b, v% u; r while(size) { read_buff = *(__IO uint32_t*)addr; //从 FLASH 读取数据到接收缓冲数组 addr = addr+4;/ c. j5 @/ X9 @ i++;# E' ^4 N9 e: A/ Q' o size--;" d$ T7 u0 Y$ L7 t }) {8 ]; r4 `8 Y& m0 [ memcpy(data,read_buff,SIZE); //将数据从接收缓冲数组转移到接收数组 return 0; }4 A7 e& J! V* r/ J6 a+ p 主要代码:$ q# Y/ U9 M: w3 P0 j5 W9 d; B uint8_t data[8]={0,0,0,0,0,0,0,0};+ r9 C ^1 M1 Q* U/ b- X( A char buf[30]=""; FLASH_Read(data,FLASH_PAGE_127,8);//8 字节 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);! h; y* v2 l. C0 n7 _; }5 @! } y uint32_t UID1=READ_REG(*((uint32_t *)UID_BASE)); uint32_t UID2=READ_REG(*((uint32_t *)(UID_BASE+4U)));8 e9 J0 \9 P' \3 L0 A 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); 0 w* N* m5 d3 L) b. h 实验现象:0 S& ~5 O( |2 w: w$ m 下载烧录后可以观察到 LCD 显示 FLASH 测试次数以及芯片的 ID。 ![]() ▲ 实验现象 第十二、单总线实验 实验目的:掌握和熟悉常用的单总线通信,包括 DS18B20,DHT11 读写应用。 CubeMX 配置如下,保存后生成对应的配置代码:( Y: n$ u+ U& {8 ] q8 A8 |3 G) `, o ![]() # \" E: M% g6 s! t ▲ 图 3.12.1 CubeMX 进行 GPIO 输出配置 x' Q, t5 \2 @7 n2 p6 G& T% i 本实验进行单总线读取 DS18B20 和 DHT11。使用 CUBEMX 配置 IO 为输出模式。, T: a& l; u+ `/ z2 t 1 F" y- H& Y% B/ D, e0 D4 ?3 ^ 读取 DS18B20 核心代码:/ n+ e0 f6 r/ P' j" h/ y/ a /* 函数名:DS18B20_IO_OUT * 功能:初始化 DS18B20 的 GPIO 为输出模式 * 输入:无 * 输出:无 * 备注:无 */ void DS18B20_IO_OUT(void ) { GPIO_InitTypeDef GPIO_InitStruct = {0};6 g2 Z$ @$ `4 z5 v9 w: l% v3 y GPIO_InitStruct.Pin = GPIO_PIN_9;9 _/ w1 L2 H; A4 }9 U ~- y GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP; GPIO_InitStruct.Pull = GPIO_NOPULL;* @) I0 t0 D* i5 @, D GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW; HAL_GPIO_Init(GPIOD, &GPIO_InitStruct);' B1 s1 S. p" L3 G; } } /* 函数名:DS18B20_IO_OUT* J5 q9 }+ L9 i$ @ * 功能:初始化 DS18B20 的 GPIO 为输入模式: Q- X: L' G# e- p * 输入:无 * 输出:无* \4 C+ L2 S# d * 备注:无: s4 g& Q6 ~5 S) W */void DS18B20_IO_IN(void ) { GPIO_InitTypeDef GPIO_InitStruct = {0}; GPIO_InitStruct.Pin = GPIO_PIN_9;$ \+ q% @- }# y4 J3 ?( p GPIO_InitStruct.Mode = GPIO_MODE_INPUT;/ _; n# T1 y9 |" Z GPIO_InitStruct.Pull = GPIO_NOPULL;, t/ T* d0 [) w+ c* G* ?& j# N) Q }/ J GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;) m& t- X4 c" h! `5 w3 _ HAL_GPIO_Init(GPIOD, &GPIO_InitStruct); }3 U/ c5 u. v+ c /* 函数名:DS18B20_Rst * 功能:复位 DS18B20# X# g3 N/ ^4 q+ I& D1 r* m6 Z * 输入:无 * 输出:无" n2 D Z6 I1 P( T * 备注:无1 u3 v/ ?2 S N! q$ p' e: `- E( q3 M */9 Y$ y9 M/ P' K% S void DS18B20_Rst(void)$ `# `4 _ s' [ y$ G+ G { DS18B20_IO_OUT(); //SET PG11 OUTPUT DQ_OUT_LOW();6 ~8 {. C5 v0 h //拉低 DQ delay_us(750); //拉低 750us DQ_OUT_HIGH(); //DQ=1 delay_us(15);! B& e- n6 f: D6 M3 r( q //15US } /* 函数名:DS18B20_Check * 功能:等待 DS18B20 的回应 * 输入:无 * 输出:返回 1:未检测到 DS18B20 的存在 返回 0:存在6 f" g0 i7 `$ V" J7 } * 备注:无 */ uint8_t DS18B20_Check(void) {9 k' P" t! L8 W' C" ? uint8_t retry=0;. `' ?, h( \/ I% S# W/ O& [# u6 ` DS18B20_IO_IN();1 |" `. S, ~( b$ s( G% C //SET PG11 INPUT while (DQ_GET_IN()&&retry<200), O' E* q3 o& W7 e2 F1 ^ {$ K- n& V! S5 F4 `7 S retry++;delay_us(1);4 Y$ M3 n4 J7 U9 Q }; if(retry>=200)return 1; else retry=0; while (!DQ_GET_IN()&&retry<240). O, ]7 T# c. S, V7 b+ K2 ^ { retry++; delay_us(1); };$ j, R4 s1 S* L& Y: L* ? if(retry>=240)return 1; return 0; }+ U7 T @9 G7 V1 K. s /* 函数名:DS18B20_Read_Bit% j$ u, L7 \) x8 R* {" t* y4 O2 C * 功能:从 DS18B20 读取一个位 * 输入:无6 H" K; x+ V C+ y * 输出:返回值:1/09 E* g$ p$ H# Q; p1 z! [8 c$ T * 备注:无 */7 T- p2 K0 d& z3 |7 I uint8_t DS18B20_Read_Bit(void) { uint8_t data; DS18B20_IO_OUT(); //SET PG11 OUTPUT- a9 @0 o* g3 S, k' _ f DQ_OUT_LOW();delay_us(2);; V6 V9 T6 K" c; u/ L DQ_OUT_HIGH();6 q4 `) j4 Y/ Y8 r0 K DS18B20_IO_IN(); //SET PG11 INPUT- j; R& V1 t4 J delay_us(12);/ ^( _% K4 s2 q H. g/ b U if(DQ_GET_IN())data=1;; ^9 V* E5 o' ]0 M1 T3 C) \; C else data=0;7 ? h, P/ o1 I, ?& |, ? J7 N delay_us(50);) H ^* a# F8 M9 v+ a h, I4 M/ U return data; } /* 函数名:DS18B20_Read_Byte. t. c6 l& ~; x) X * 功能:从 DS18B20 读取一个字节 * 输入:无4 E5 m4 V8 I# s/ u% \/ ^ U% V0 {: W * 输出:返回值:读到的数据% s" H; W+ j8 V! _6 } * 备注:无9 z' T, O; _, a& |. n! x+ { */ uint8_t DS18B20_Read_Byte(void) { uint8_t i,j,dat;% l* }. V4 @+ D3 t6 } dat=0;0 D6 l8 `( v" Q8 z0 w) \ for (i=1;i<=8;i++)9 f$ t' s$ h) {6 Q+ j { j=DS18B20_Read_Bit(); dat=(j<<7)|(dat>>1); } return dat; }7 `9 w! T: P Z% Q, ` /* 函数名:DS18B20_Write_Byte * 功能:写一个字节到 DS18B20 * 输入:dat:要写入的字节, B% U, \; C; e5 _9 ^ * 输出:无 * 备注:无 */ void DS18B20_Write_Byte(uint8_t dat)5 k) }5 l* `+ L1 d {9 k# I4 Y; m7 y uint8_t j;) m/ z. s: O! \8 M! ]6 h uint8_t testb; DS18B20_IO_OUT(); //SET PG11 OUTPUT;1 E/ x8 u: u* L$ d for (j=1;j<=8;j++) {2 k! z! n* u m- Z$ R; s testb=dat&0x01;& j. Z) D7 y+ F# R$ S dat=dat>>1; if (testb) { DQ_OUT_LOW(); // Write 1 d, n. \* m. c6 N9 F5 P: D+ C+ k delay_us(2); DQ_OUT_HIGH(); y3 ~# X( M& @! j5 A delay_us(60);$ A ^9 g, ?! ~# A } else* c, t2 Q+ t( B! M; @ { DQ_OUT_LOW(); // Write 0 delay_us(60); DQ_OUT_HIGH(); delay_us(2);" F3 z, M* j" S }$ |4 d0 k* [) d7 L1 l" F5 w } }$ {" E6 s5 P% q" y /* 函数名:DS18B20_Start * 功能:开始温度转换2 w- u6 r1 f9 f' W * 输入:无 * 输出:无; w; C/ |- p5 {7 @- { * 备注:无 */: t. R& ~4 _; ^, I8 N$ h // void DS18B20_Start(void) {2 B0 T0 P0 @1 n( C$ i. r DS18B20_Rst();; D( x/ [7 d- `% `+ t# L4 } DS18B20_Check();' Z) K; H6 R; I3 k4 _% t+ R DS18B20_Write_Byte(0xcc);// skip rom DS18B20_Write_Byte(0x44);// convert3 r& F' ?; _5 q$ G$ y+ b }/ e+ A2 W' h- M1 G" _. P5 V: _, z8 \ /* 函数名:DS18B20_Start+ X9 a0 C4 i: i2 ^8 B; w * 功能:从 ds18b20 得到温度值 精度:0.1C * 输入:无 * 输出:返回值:温度值 (-55.0~125.0)1 O) }5 Y9 ^2 [, h0 k * 备注:无 */- t8 L* d/ X7 Y8 k! d9 C0 ] short DS18B20_Get_Temp(void)5 f+ ^) \+ O0 Y* T. ~ { uint8_t temp;- Z0 H2 A. `1 B# v7 q4 k0 ? uint8_t TL,TH;6 s0 H( i- \- e X short tem;DS18B20_Start (); // ds1820 start convertDS18B20_Rst();& R# u( y, M% j2 V) ~" o) b DS18B20_Check();: G O/ T; g3 |* h, r4 S DS18B20_Write_Byte(0xcc);// skip rom$ g8 R- l, P7 U, x DS18B20_Write_Byte(0xbe);// convert! |; x9 x$ c5 h TL=DS18B20_Read_Byte(); // LSB TH=DS18B20_Read_Byte(); // MSB if(TH>7) {& j5 Y0 j% {' n3 d- u9 w1 f7 V TH=~TH;7 x$ Z, W8 C: A) Q, H% y! I6 ^ TL=~TL; temp=0; //温度为负 }else temp=1;+ A, P! D3 z6 `( H$ R! X4 I //温度为正 tem=TH;; W( R$ K' `8 }- e //获得高八位9 ^( a, n* Z7 k" l tem<<=8;tem+=TL;) w# W% d1 k! l* P1 C0 Z //获得底八位 tem=(float)tem*0.625;/ Y& E$ G% r6 I0 i3 D& T //转换* g+ Y. j8 J) L9 Q9 N char buf[20]=""; p' F' k2 ]% v! F! a if(temp)snprintf(buf,10,"Temp:%.1f",tem/10.0);: o% ]' N; M9 c: E |. q else snprintf(buf,10,"Temp:%.1f",tem/10.0); LCD_PutString(10, 10, buf, White,Blue,1); if(temp)return tem; //返回温度值& O Q* f( l8 p" `! @ else return -tem; }" x- q, Q' ?' m/ y 读取 DHT11 核心代码: /* 函数名:delay_us * 功能:微秒延时 * 输入:delay 延时多少微秒 * 输出:无 * 备注:无/ h# r/ T( t7 Z9 C */ #define CPU_FREQUENCY_MHZ 170 // STM32 时钟主频 void delay_us(__IO uint32_t delay)% k4 A- s; S+ [- {# L) H# a { int last, curr, val;4 ?' u4 U" d2 w& X int temp;# H9 o9 C+ m4 C$ k/ f) d while (delay != 0)# E1 B) H. ^- j {" d* ?4 X& @5 p6 _$ l/ o temp = delay > 900 ? 900 : delay; last = SysTick->VAL; curr = last - CPU_FREQUENCY_MHZ * temp;, K+ c3 u2 d+ N: \( f, c if (curr >= 0)& D' [- S2 m" m2 j { do {* I# }5 E/ h+ C8 S, k val = SysTick->VAL; } while ((val < last) && (val >= curr));( J0 D9 c' N8 Y3 Z% _8 x } else {' V) y; u' r# f9 e) c7 X$ k curr += CPU_FREQUENCY_MHZ * 1000;: p" B8 K' I- E5 H5 I( o- o9 g: Z do {+ D! a$ G- J. [5 z val = SysTick->VAL; } while ((val <= last) || (val > curr));( |& X3 l X) y- T } delay -= temp; } }) K7 {+ L7 s! O. H unsigned int rec_data[4];* R! v/ ?" B8 N" G! f" r. X /* 函数名:DH11_GPIO_Init_OUT: |; D6 A1 C( I; n1 E * 功能:初始化 DHT11 的 GPIO 为输出模式+ Z. T9 v0 m4 i2 B * 输入:无 * 输出:无 * 备注:无' d/ n7 L+ N. I b */( x$ A' Y8 v/ R. Z7 O void DH11_GPIO_Init_OUT(void)1 a$ R# O* L" c4 C { GPIO_InitTypeDef GPIO_InitStruct = {0};" K R6 r2 P" Z+ L GPIO_InitStruct.Pin = GPIO_PIN_9; GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;) r1 l, K0 m! S/ o S. ]9 d GPIO_InitStruct.Pull = GPIO_NOPULL; GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW; HAL_GPIO_Init(GPIOD, &GPIO_InitStruct); } // 对于 M0 来说,是输入 X2 \" z/ D7 Q) Z /* 函数名:DH11_GPIO_Init_IN * 功能:初始化 DHT11 的 GPIO 为输入模式 * 输入:无1 W9 Y+ _ O6 o- J6 Y( y( P * 输出:无. r+ {) f, t& i1 @' U- x * 备注:无 */ void DH11_GPIO_Init_IN(void)6 b' n# {# x0 I* c- r {1 H! Q: J& D' X$ U- X i% h* l GPIO_InitTypeDef GPIO_InitStruct = {0};; L# j/ w- g1 f1 J GPIO_InitStruct.Pin = GPIO_PIN_9; GPIO_InitStruct.Mode = GPIO_MODE_INPUT;- w* w9 y4 [5 t+ } GPIO_InitStruct.Pull = GPIO_NOPULL;. A( ]7 r7 ?8 J GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;1 V8 n9 i0 c! O5 ?6 E* g0 B HAL_GPIO_Init(GPIOD, &GPIO_InitStruct); // DL_GPIO_initDigitalInput(GPIO_DQ_dht11_and_ds18b20_IOMUX);//配置为上拉输入$ A5 V& _! z" V8 n7 n2 A" D9 B } // 主机发送开始信号5 I0 l5 Q+ ]% x Y/ H( u0 j; i: C; G /* 函数名:DHT11_Start5 S( H3 }( C, A i. Q * 功能:主机发送开始信号 * 输入:无- C" Q$ h, U* [7 H * 输出:无5 H0 Y6 k @9 Z, h * 备注:无5 }! S+ q' E! | */ void DHT11_Start(void) {9 F8 N2 a3 \$ ^/ p' ^# F DH11_GPIO_Init_OUT(); // 输出模式3 D: \- r- H- R1 F2 s5 R dht11_high; // 先拉高 delay_us(30); dht11_low; // 拉低电平至少 18ms" L6 P$ ?9 q9 Z6 n3 S HAL_Delay(20);" ]5 D/ T7 w/ f6 l+ d dht11_high; // 拉高电平 20~40us- T! W; i$ W" I9 c N: ~2 [ DH11_GPIO_Init_IN(); // 输入模式 delay_us(30); } // 获取一个字节% W+ P( C! n/ ? /* 函数名:DHT11_Rec_Byte * 功能:获取一个字节- q0 e2 B6 _* G$ y+ m * 输入:无 * 输出:读取的字符 * 备注:无6 `' w0 X0 @1 o' k' T# r */. B+ A6 |1 e6 S- p& T5 s char DHT11_Rec_Byte(void) {+ \" ^2 C' o3 n4 D8 C1 ]0 c7 O unsigned char i = 0;3 i6 _8 ~, r- b1 q8 S unsigned char data; for (i = 0; i < 8; i++) // 1 个数据就是 1 个字节 byte,1 个字节 byte 有 8 位 bit) U, W; d% N9 u) K9 ]/ A; ] { while (dht11_read == 0)" x8 l' i L0 T! g6 ]5 m ;' @5 `8 ~, r# }5 ]* a // 从 1bit 开始,低电平变高电平,等待低电平结束 delay_us(30); // 延迟 30us 是为了区别数据 0 和数据 1,0 只有 26~28us data <<= 1; // 左移 if (dht11_read == GPIO_PIN_SET) // 如果过了 30us 还是高电平的话就是数据 1" c# ~* Q$ Q0 u: a7 Y {2 K- E0 N3 k! y( s6 V2 S5 o data |= 1; // 数据+1( ]8 i) t" l5 [3 r- P9 i }+ h" ^( L. V6 p4 O% H( G* E6 l while (dht11_read == GPIO_PIN_SET) ; // 高电平变低电平,等待高电平结束6 [$ q! i: B$ i7 V" @( i }; I5 [! P3 z/ s return data;6 O6 I/ B3 _1 ?# w }1 J. ~2 \# z1 {1 E7 C* _ d6 G! V // 获取数据 /* 函数名:DHT11_REC_Data0 ^. N$ G$ M/ G8 n) ^% w1 B5 J* S * 功能:获取 DHT11 数据并打印 * 输入:无 * 输出:无 * 备注:无/ N! X3 R' M" \ */ void DHT11_REC_Data(void)" ?9 c% Q, ^2 d6 |$ P. O! t {" \/ w( S4 [( e7 N; \ unsigned int R_H, R_L, T_H, T_L;3 A2 K1 n0 y3 j1 |) z0 Z unsigned char RH, RL, TH, TL, CHECK;" b0 a2 I2 _7 m6 o+ }+ X DHT11_Start(); // 主机发送信号 //dht11_high; // 拉高电平 if (dht11_read == 0) // 判断 DHT11 是否响应 S: f- z6 V0 Z' f$ r* }6 m4 o {" y |; z3 k' Z& V while (dht11_read == 0); // 低电平变高电平,等待低电平结束 while (dht11_read == GPIO_PIN_SET); // 高电平变低电平,等待高电平结束3 {- l, q$ P( F/ d- c R_H = DHT11_Rec_Byte(); R_L = DHT11_Rec_Byte(); T_H = DHT11_Rec_Byte(); T_L = DHT11_Rec_Byte();5 K6 m, T2 ~( |) z/ M CHECK = DHT11_Rec_Byte(); // 接收 5 个数据% C1 f- {. S7 u+ ~* Z6 C( ^2 a0 }& X //DH11_GPIO_Init_OUT(); //dht11_low; // 当最后一 bit 数据传送完毕后,DHT11 拉低总线 50us0 t% I& J: \' o" p delay_us(55); // 这里延时 55us6 f. H: F8 F+ I+ \! g( T. H" J //dht11_high; // 随后总线由上拉电阻拉高进入空闲状态。 if (R_H + R_L + T_H + T_L == CHECK) // 和检验位对比,判断校验接收到的数据是否正确 {# i( x( ^. |. I: T0 _ RH = R_H;8 a' c$ x5 S- {* i RL = R_L; TH = T_H;# c9 f, ~4 C" O TL = T_L;% C* E% J3 z! ]2 J9 E4 j }" ^) D; E B/ [+ l" [$ V4 ? } rec_data[0] = RH; rec_data[1] = RL; rec_data[2] = TH; rec_data[3] = TL; char buf[20]="";8 a; z7 }7 t1 X snprintf(buf,10,"Temp:%d.%d",TH,TL);4 k( b( g+ C E8 F' f LCD_PutString(10, 10, buf, White,Blue,1); snprintf(buf,10,"Hum:%d.%d",RH,RL); LCD_PutString(10, 30, buf, White,Blue,1);} / V# v9 a% f$ X0 Z! O" R 主要代码:( I' r4 a0 {( B6 s- ~ while(1){1 [# k: e4 w# |, t/ r DHT11_REC_Data();//DHT11 读取, c2 s; k+ l# J+ \/ t* [1 G //DS18B20_Get_Temp();//18B20 读取" m4 ?2 e3 \* h5 n0 ^: B HAL_Delay(1000);! K7 _* r3 T/ [1 S, h- Y$ J }$ r; x6 s& g: W% B4 K p* x 1 x. \* ^ Q- w9 u4 f7 s 4 F7 [& L3 h$ B- @! \( w* O3 `2 H3 I 实验现象: 下载烧录后可以观察到 LCD 显示 DS18B20 测试的温度,或者 DHT11 测量的温度和湿度。( }8 b; E4 l$ N* ` ![]() ▲ 图 3.12.2 实验现象) G" O1 ^/ y1 v' `5 L 第十三、独立看门狗6 G, F) z8 t7 F* e 实验目的:掌握和熟悉独立看门狗用法,包括喂狗操作等。 CubeMX 配置如下,保存后生成对应的配置代码: ![]() ▲ 图 3.13.1 CubeMX 进行独立看门狗配置% z4 }- f: `: O; r2 o% X 本实验进行独立看门狗配置。使用 CUBEMX 配置 IO 为输出输入模式实现 LED 指示和按键读取,GPIO 配置参考上文。程序中使用按键喂狗,如果没在 1s 内喂狗,系统将会自动复位。! f' ^' B" |' x! s ( d; N" K8 a0 Z 主要代码:& {% f2 ^. k4 p* ?& C$ o3 e# @ while (1) { if(HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_4)==0) {7 P3 Y6 K8 V* J: o! m HAL_Delay(5);9 X- p1 P; Q P; G! ?8 s if(HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_4)==0)6 p; `8 j( K5 v$ w5 Y. ? ~ { HAL_IWDG_Refresh(&hiwdg);, S6 L( W& ]* e$ T' M HAL_GPIO_TogglePin(GPIOD,GPIO_PIN_10);* f5 {1 X F1 \" {4 h while(!HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_4)); } } /* USER CODE END WHILE */ /* USER CODE BEGIN 3 */5 q3 \5 I& z* q0 ^- y4 { } " c- Q, o0 [: y. O: Q7 i 实验现象:1 U$ t/ t8 J4 l2 \( R6 \1 N 下载烧录后可以观察到如果在一定时间内进行喂狗操作,系统会复位,LCD 重新加载。 ![]() ▲ 图 3.13.2 实验现象& @4 M `# b, c; M! q4 n 5 C8 D1 D- J% K6 D8 t" u 转载自: AI电堂 如有侵权请联系删除 : g/ i8 h0 ~ I. T' M+ p/ Z 7 o, t- h* Y- W7 x 4 z; p3 g% X3 G" i |
STM32固件库分享,超全系列整理
STM32G系列RS485自动收发控制以及自适应波特率实战
【学习指南】基于STM32G474VET6 开发板实验经验分享(二)
【学习指南】基于STM32G474VET6 开发板基础实验经验分享一
【学习指南】基于STM32G474软件平台安装与使用教程
【学习指南】基于STM32G474VET6 开发板硬件资源解析
STM32 Explore | 基于STM32G474的STM32Cube生态系统线下培训
STM32G47x 双 Bank 模式下在线升级
基于STM32G473ZET6开发板设计经验分享
详细讲解STM32G4的软件工具和环境搭建