|
第十、COMP 实验! Z; ?+ s% k, ^8 B4 j. A6 v$ y0 W% M0 K 实验目的:掌握和熟悉 G474 内部的模拟比较器用法,包括触发方式以及与内部 DAC 级联使用等。 1、软件读取 COMP 结果实验" q' c; H! a! {; s% M CubeMX 配置如下,保存后生成对应的配置代码:
/ q2 G. O+ U" h6 ~ B$ o- K ▲ CubeMX 进行 COMP 配置$ D1 p$ `! D" R( U8 R$ j 本实验使用软件读取 COMP 比较结果,不需要配置触发,为了使比较结果更加直观,开启外部比较结果输出。" |+ B- O. n7 K& C- O& d8 u ) }8 H( W" ^2 ?) f3 T" u , d: M, R6 ^( x' b" D/ l7 |% m 相关操作函数说明: HAL_StatusTypeDef HAL_COMP_Start(COMP_HandleTypeDef *hcomp) 功能:开启比较器; 参数 1:比较器句柄,根据需要填写;3 o7 c% p/ k$ A7 K: ` 返回:操作结果,HAL_OK 或 HAL_ERROR; 示例:HAL_COMP_Start(&hcomp3);// 开启 COMP3HAL_StatusTypeDef HAL_COMP_Stop(COMP_HandleTypeDef *hcomp)8 y9 D: o( H O, w2 R 功能:关闭比较器;; E a' \: d# \% w @. v8 j( W% L 参数 1:比较器句柄,根据需要填写;0 f( f% A( _; q E. B 返回:操作结果,HAL_OK 或 HAL_ERROR; uint32_t HAL_COMP_GetOutputLevel(const COMP_HandleTypeDef *hcomp) 功能:读取比较器输出电平; 参数 1:比较器句柄,根据需要填写;) J& D/ s: l8 y1 K# c7 R$ m3 X6 L 返回:比较结果,COMP_OUTPUT_LEVEL_LOW 或 COMP_OUTPUT_LEVEL_HIGH; 示例:result = HAL_COMP_GetOutputLevel(&hcomp3); //软件读取比较结果 0 R0 b: u, i- N! y0 u7 l- M 核心代码:; k7 e. q/ a/ L5 t if(HAL_COMP_Start(&hcomp3) != HAL_OK)7 O3 A, N# p8 X9 d/ @ //开启比较器+ o: U/ F' n P/ A: j9 J1 L7 s {/ p! Q9 |9 {: y8 e6 p2 P; k Error_Handler(); } while (1)! f: t# ~: u+ E7 V; H4 ? {4 z4 N+ E9 E* H8 e result = HAL_COMP_GetOutputLevel(&hcomp3); //软件读取比较结果 if(result == COMP_OUTPUT_LEVEL_HIGH)//比较结果为 1,即 INP 大于 INM9 D1 i- S3 X& C) |1 c$ w& \ { HAL_GPIO_TogglePin(GPIOD,GPIO_PIN_11);//翻转 LED2 }7 U* ^" w+ X' Z4 v* [% l HAL_Delay(100);4 a/ z3 Z% S ?' U3 ~/ f4 ?2 K }& A3 y* m; }+ Y! G. e ( s5 K& Y! Z) C! ]- J9 Z) Y 以上为 main 函数中外设初始化结束后的部分首先开启比较器,然后在主循环中每 100ms读取一次比较结果,如果结果为 1,即正相输入大于反相输入,则进行 LED2 翻转。 实验现象: 下载烧录后可以观察到拨动拨盘电位器,或者遮蔽光敏电阻时可以改变比较结果,CMP 亮时,LED2 状态不变,CMP 灭时,LED2 闪烁。1 `0 C+ v& x7 z/ ? 2、中断读取 COMP 结果实验 CubeMX 配置如下,保存后生成对应的配置代码:
▲ CubeMX 进行 COMP 配置 * p( x% {# Z! P( ]( _ 本实验使用中断读取 COMP 比较结果,在比较结果变化的上升沿和下降沿都触发中断,为了使比较结果更加直观,开启外部比较结果输出。 t0 G4 j3 P8 f# g z- k0 C 核心代码: if(HAL_COMP_Start(&hcomp3) != HAL_OK)0 E- U4 L0 V. K3 x5 p7 f! x4 { //开启比较器8 e2 Z$ c: j, `% Z" A {& l6 N, J7 Q, v Error_Handler(); } 以上为 main 函数中外设初始化结束后的部分,这里只需要开启比较器即可,使用函数与上例相同。; u; z5 O3 j7 T6 R$ @0 E6 j 3 K: V1 r0 S W9 p) E void HAL_COMP_TriggerCallback(COMP_HandleTypeDef* hcomp) {" D/ Z/ Z- X( p* |6 b if(hcomp->Instance==COMP3) y1 k& |' g& g. V { uint8_t temp; temp = HAL_COMP_GetOutputLevel(&hcomp3);//读取比较结果4 U& N. Q9 D8 C9 N0 S; F( X if(temp == COMP_OUTPUT_LEVEL_HIGH)//结果为 1,上升沿触发 {7 ~" ]1 ~. s8 T1 ?/ T j; j: J; V& R HAL_GPIO_WritePin(GPIOD,GPIO_PIN_10,GPIO_PIN_RESET);//点亮" `" e8 o/ E p% j! h8 m LED1 HAL_GPIO_WritePin(GPIOD,GPIO_PIN_11,GPIO_PIN_SET);//熄灭 LED2 }9 Y G" n! m: y else { HAL_GPIO_WritePin(GPIOD,GPIO_PIN_10,GPIO_PIN_SET);//熄灭 LED1 HAL_GPIO_WritePin(GPIOD,GPIO_PIN_11,GPIO_PIN_RESET);//点亮 LED2# [/ M5 D' d' T! M3 V# t* o9 S } }: V6 k+ m- R% W0 n9 D5 j% J }' G* e! _9 C" `) V 9 O% r. R$ g v1 \( o / J2 X% K2 T$ U# l' G 以上为 COMP 触发中断回调函数,该回调函数为比较器共用,需要判断中断源,如果开启了多个边沿,还需要判断具体边沿。实质上,COMP 中断触发与 EXTI 类似,实际就是将COMP 比较输出作为一个 IO 映射到 EXTI 进行中断。 ) Q; U9 S7 E, p) [: L 实验现象:# c2 \ i4 T! D- i 下载烧录后可以观察到拨动拨盘电位器,或者遮蔽光敏电阻时可以改变比较结果,CMP 亮时,LED1 灭,LED2 亮,CMP 灭时,LED1 亮,LED2 灭。 ' e2 j2 \; {6 j7 a( b 6 p; ~3 A" v0 |9 n& y0 T3 u 3、内部 DAC 级联比较实验 CubeMX 配置如下,保存后生成对应的配置代码:
▲ CubeMX 进行 COMP 配置1 v( N5 c5 i4 ?' } H( w1 z
▲ CubeMX 进行 DAC 配置: b: W! o# J- j 9 A7 a7 v, K& c$ \8 v- n1 Z . Z6 c" U4 A# ~- m1 {, d4 R* _ 本实验使用软件读取 COMP 比较结果,正相输入为光敏电阻,反相输入使用 DAC3 的 OUT1作为比较电压,为了使比较结果更加直观,开启外部比较结果输出。5 A. w, j- P" n* I" Z ! x! P) [* f$ V8 i" a* r5 A+ o1 E 核心代码:; L/ \! _* s K void DAC3_CH1_Set_Vol(uint16_t vol) { double temp=vol;" p) Z# `/ B% g$ g. _$ \+ ~ temp/=1000; temp=temp*4096/3.3; HAL_DAC_SetValue(&hdac3,DAC_CHANNEL_1,DAC_ALIGN_12B_R,temp);//12 位右对齐数据格式设置 DAC 值 } ; Q! j) N( N' Z" N) m- |5 S1 Y 4 S9 f$ U/ L B: p* s C3 O4 m' u 以上为 DAC 设置函数,通过该函数可以将输入的电压快速转换为 DAC 所需的寄存器数据。2 s7 B$ ]# y6 {: q $ j; v4 t7 f2 h DAC3_CH1_Set_Vol(500);//DAC 输出设置为 500mv HAL_DAC_Start(&hdac3,DAC_CHANNEL_1);//生效 DAC 输出6 w. i9 W& k. ~ if(HAL_COMP_Start(&hcomp3) != HAL_OK)0 m; h2 J7 I; ^" z* {/ N7 j9 G //开启比较器 {$ T! S1 W4 H+ R" u2 ~7 ? Error_Handler(); }4 z3 N* U/ c1 A1 C( N1 p- Z while (1) { result = HAL_COMP_GetOutputLevel(&hcomp3); //软件读取比较结果 if(result == COMP_OUTPUT_LEVEL_HIGH)//比较结果为 1,即 INP 大于 INM { HAL_GPIO_TogglePin(GPIOD,GPIO_PIN_11);//翻转 LED20 X9 A R7 T. c% N+ u: n } HAL_Delay(100); }9 z+ {, P* f; {$ v# I. ^ 以上为 main 函数中外设初始化结束后的部分,首先设置好 DAC 输出电压,开启 DAC,然后开启比较器,最后在主循环中每 100ms 读取一次比较结果,如果结果为 1,即正相输入大于反相输入,则进行 LED2 翻转。: r% \) X$ ?8 O1 |! q2 k . z: W/ J+ b2 U; s 实验现象:& g. d$ U9 |3 `$ q- u# p6 D 下载烧录后可以观察到遮蔽光敏电阻时可以改变比较结果,CMP 亮时,LED2 状态不变,CMP 灭时,LED2 闪烁。. L: u% x: X+ g) p8 S ; W0 `- j" }4 d* Y 第十一、FLASH 实验! ]3 v/ X2 @% Z0 p 实验目的:掌握和熟悉 G474 内部的 FLASH 用法,包括 FLASH 读写应用等。FLASH 读取无需进行配置。 1 K; s( K, Y2 X$ M2 T4 [, W% a 核心代码: /* 函数名:FLASH_Write4 \; G& K7 I0 T+ `; [2 F * 描述 :flash 写入数据 * 输入 :data 写入数据地址 addr flash 地址 size 写入字节数 * 输出 :无 * 调用 :FLASH_Write(data,FLASH_PAGE_127,8);//8 字节 */+ S. O) s$ Y" U) _ uint8_t FLASH_Write(uint8_t* data,uint32_t addr,uint16_t SIZE)) Z6 t/ Q7 T- k/ A { FLASH_EraseInitTypeDef EraseInitStruct;//定义擦写操作结构体 uint32_t SECTORError = 0;+ D8 J2 ~ F- D uint64_t write_buff[10];//写入缓冲数组 uint8_t i = 0;& A/ R& W. u6 C, W) B8 M5 v9 N uint8_t size = SIZE/8;//8 字节写入次数 memcpy(write_buff,data,SIZE);//将输入的数组移动到写入缓冲区: v! o/ m6 j; ~9 @ HAL_FLASH_Unlock();//解锁0 R k7 [2 `) C) r+ R6 O EraseInitStruct.Banks = FLASH_BANK_1; //存储区 1 EraseInitStruct.TypeErase = FLASH_TYPEERASE_PAGES;//页擦除: @5 \' @$ Y' @7 s+ t5 r9 C0 R EraseInitStruct.Page = (addr-0x08000000)/FLASH_PAGE_SIZE;//擦除页& M1 h v: S: `. N% f5 i. e5 W EraseInitStruct.NbPages = 1;//擦除页数* u3 J7 Q6 U# I0 g# e3 Q9 u if(HAL_FLASHEx_Erase(&EraseInitStruct,&SECTORError) != HAL_OK) //擦除页% @5 v: M& c, w3 O; r { return 1;//擦除失败1 J& I7 L3 e2 e }; C3 c% @; V8 w$ o. w while(size)0 T7 b5 V. m4 d/ q$ \- Y { if(HAL_FLASH_Program(FLASH_TYPEPROGRAM_DOUBLEWORD,addr,write_buff)!= HAL_OK)//双字写入 { return 2;//写入失败: q. b1 y! q! c, f6 v }% |- y/ v, O% c- [! | addr = addr+8; i++;! z+ k# e! y% w4 [) {5 H size--;* {4 [3 w/ {! K% z- ]+ e% s } HAL_FLASH_Lock(); //锁定 FLASH return 0; } /* 函数名:FLASH_Read * 描述 :flash 读取数据 * 输入 :data 读取数据地址 addr flash 地址 size 读取字节数. E1 |' v4 E* @8 A3 Z5 [# y * 输出 :无: q0 S; C' A& G) D; F" I, j * 调用 :FLASH_Read(data,FLASH_PAGE_127,8);//8 字节( y- O% z K `, i6 e. }8 ` */7 D9 t: ^" |: [& w* t3 g4 A- { uint8_t FLASH_Read(uint8_t* data,uint32_t addr,uint16_t SIZE): w8 _( ^' A( f, k: V6 d {0 y! J" ~8 i0 P3 x+ i uint32_t read_buff[10]; //接收缓冲数组% X. |! O7 I' K% W8 S uint8_t i = 0;9 w, m5 h1 U! b" S! F, }- s! H uint16_t size = SIZE/4; //接收次数2 w$ v3 N3 k* D! [5 h while(size) {) k$ i4 [- q# i9 E% [% O7 Y read_buff = *(__IO uint32_t*)addr; //从 FLASH 读取数据到接收缓冲数组( Y7 S* n1 W- I, _8 E9 h addr = addr+4; X; R4 r; i4 w9 V i++; size--; }" x8 a4 v0 G9 V5 [! u4 M) @ memcpy(data,read_buff,SIZE); //将数据从接收缓冲数组转移到接收数组- ^) k9 O1 f* t7 O- O1 W return 0;' i" r Y, E. ]( v( Y d } . W; t% k1 E! B2 V- a X ; E6 r! @2 W5 ` 主要代码:8 M% }6 ?( m( A uint8_t data[8]={0,0,0,0,0,0,0,0};- q6 {/ v: y* ?. A4 u# \ k char buf[30]="";0 l: ~) E$ ?# ]! w, T 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]);3 R5 L- ?, a% e LCD_PutString(10,30,buf,Red,White,0);) C- Y" U1 h* C( ]) x1 P uint32_t UID1=READ_REG(*((uint32_t *)UID_BASE));& V5 V% M7 W; n& L' @ uint32_t UID2=READ_REG(*((uint32_t *)(UID_BASE+4U)));! m. j4 W! X0 r6 K& y' G- ^ uint32_t UID3=READ_REG(*((uint32_t *)(UID_BASE+8U)));, a$ P7 L0 V. u" e7 q snprintf(buf,30,"UID:%0x-%0x-%0x",UID1,UID2,UID3); LCD_PutString(10,60,buf,Red,White,0); 4 R: B1 H7 p9 A8 l* w 实验现象: 下载烧录后可以观察到 LCD 显示 FLASH 测试次数以及芯片的 ID。
▲ 实验现象 3 q- d, x& f9 X4 u) I) v8 L 第十二、单总线实验 实验目的:掌握和熟悉常用的单总线通信,包括 DS18B20,DHT11 读写应用。6 K' C2 h2 g/ o0 w. l: b% e CubeMX 配置如下,保存后生成对应的配置代码: F* C' O6 d; ]! m( S8 h( F8 h
! Q5 a5 P( x* O+ c& l4 q ▲ 图 3.12.1 CubeMX 进行 GPIO 输出配置 : \: x ~9 O2 f1 G% [: P" M 本实验进行单总线读取 DS18B20 和 DHT11。使用 CUBEMX 配置 IO 为输出模式。 " K, l$ q+ X* [+ p n 9 l" e/ J% E: z5 ]0 }0 \ 读取 DS18B20 核心代码:5 B* i: v; n9 D I/ r$ R3 M8 }* p /* 函数名:DS18B20_IO_OUT! V1 b, A$ [; m8 D0 Y/ O! X * 功能:初始化 DS18B20 的 GPIO 为输出模式 * 输入:无0 z* \& n$ |$ W/ G* P. p8 D6 I, B * 输出:无 * 备注:无 */7 s$ P3 |3 A+ I void DS18B20_IO_OUT(void ) {, ~3 X% N7 R( p* T/ k+ @2 X GPIO_InitTypeDef GPIO_InitStruct = {0};, m; W. l J9 @- D. g. F GPIO_InitStruct.Pin = GPIO_PIN_9; GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;6 s) _# p" K# e GPIO_InitStruct.Pull = GPIO_NOPULL; GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW; HAL_GPIO_Init(GPIOD, &GPIO_InitStruct);" Z N( I$ g' o3 O, E; }! ~/ S } /* 函数名:DS18B20_IO_OUT9 l5 j/ x& i* o" j3 H * 功能:初始化 DS18B20 的 GPIO 为输入模式 * 输入:无 * 输出:无9 i5 @: M4 E# ?/ D9 o * 备注:无; U$ h2 I7 S, m: Q */void DS18B20_IO_IN(void ) { GPIO_InitTypeDef GPIO_InitStruct = {0}; GPIO_InitStruct.Pin = GPIO_PIN_9; GPIO_InitStruct.Mode = GPIO_MODE_INPUT;5 j' y" `9 ?; i7 @6 f+ B$ e7 h GPIO_InitStruct.Pull = GPIO_NOPULL; GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW; HAL_GPIO_Init(GPIOD, &GPIO_InitStruct); } /* 函数名:DS18B20_Rst * 功能:复位 DS18B20 * 输入:无! N) u. C7 j0 i& f$ D * 输出:无 * 备注:无( j6 ~2 a2 Z6 P8 k8 I6 \1 L */! K+ k8 D# M( c/ q* b" } void DS18B20_Rst(void) { DS18B20_IO_OUT(); //SET PG11 OUTPUT DQ_OUT_LOW();" D3 Y; }* A! B% \, { //拉低 DQ delay_us(750); //拉低 750us DQ_OUT_HIGH();& [* Q7 O; z1 O //DQ=1 delay_us(15); //15US! l$ u" {) ?2 g8 t- \+ A0 B2 P" ^ } /* 函数名:DS18B20_Check5 Q. W6 F( G# W/ x) E * 功能:等待 DS18B20 的回应9 D& v) u7 @6 x/ U9 a * 输入:无- A/ ^( e% I# w" Y2 K9 E * 输出:返回 1:未检测到 DS18B20 的存在 返回 0:存在6 X- G1 S6 S; q& b% f * 备注:无& P* h. n: f/ I7 x */ uint8_t DS18B20_Check(void) {7 q7 D( v3 h' p( Y2 O- i2 [' I7 _. X uint8_t retry=0; DS18B20_IO_IN(); //SET PG11 INPUT7 d: i/ _5 C! V4 @ while (DQ_GET_IN()&&retry<200)& R4 i! o) [9 y- o: y( L {1 P7 c: x9 @, W7 \% H0 d retry++;delay_us(1);" \* d3 k P- C3 ^ }; if(retry>=200)return 1;% O" ~3 h* Y: U" Y& e( | D else retry=0; while (!DQ_GET_IN()&&retry<240)! A1 R) R' ~" u$ u# Q {8 Q! g+ F( [- Z* q2 a retry++;- y. ]* x( {- y8 T delay_us(1);$ l! s/ \( ]1 A }; if(retry>=240)return 1; return 0; }6 X2 F0 ]& _" ?7 z /* 函数名:DS18B20_Read_Bit' c( d7 X+ [$ J& q+ ?4 T0 e9 X * 功能:从 DS18B20 读取一个位 * 输入:无1 S$ I5 z" e/ p! |4 o& \ * 输出:返回值:1/0 * 备注:无 */ uint8_t DS18B20_Read_Bit(void)$ V# Z+ E4 j' Q) X9 w { uint8_t data;0 Y9 l6 i8 q, k& y* r9 \2 N DS18B20_IO_OUT(); //SET PG11 OUTPUT- M6 t- V3 K6 p M% i DQ_OUT_LOW();delay_us(2);1 F! A2 A) ~0 G+ t DQ_OUT_HIGH();3 Q l! l% e) H5 n DS18B20_IO_IN();) v3 |( Y& @. ~0 \ //SET PG11 INPUT0 K1 u" R1 q# Q. f! _! i2 x' Q2 { delay_us(12);2 ]& O" C1 U+ }- D3 ? if(DQ_GET_IN())data=1;7 W, k" I8 k. e$ F' ~ z( S& V else data=0; delay_us(50); return data;, R6 f6 \/ W0 F0 ?: C } /* 函数名:DS18B20_Read_Byte) v0 G4 v/ H, s' N" M S * 功能:从 DS18B20 读取一个字节 * 输入:无. W) C. L0 E2 q- X0 v* W * 输出:返回值:读到的数据5 I: S5 r# c: e/ O1 Z9 T. v) \3 A * 备注:无 */8 O) O: W) ^- z uint8_t DS18B20_Read_Byte(void). Y" z, p" K5 y { uint8_t i,j,dat;- J7 ?3 ~# q6 ^ dat=0;$ \& m$ u) W( }# I+ P# l5 N' W9 M for (i=1;i<=8;i++) {% x# \% E7 H! F1 V j=DS18B20_Read_Bit(); dat=(j<<7)|(dat>>1); } return dat;# S4 C/ n& W2 f) X# z1 z; \ } /* 函数名:DS18B20_Write_Byte * 功能:写一个字节到 DS18B20) ]0 q6 W. r0 }3 H * 输入:dat:要写入的字节 * 输出:无; a* l2 K) n& p1 T" `# L+ t * 备注:无 */ void DS18B20_Write_Byte(uint8_t dat)4 M) h" Y6 t; c# z {# X/ v' s$ d9 t% D4 m ] uint8_t j;, b# I& e. @4 w1 @; k uint8_t testb; DS18B20_IO_OUT(); //SET PG11 OUTPUT; for (j=1;j<=8;j++) {2 y( P' p1 g$ s/ M- h$ @ testb=dat&0x01;! b6 n& z2 Z- X' S dat=dat>>1;/ U; D+ r% L1 c+ E& b! W$ X if (testb)$ j6 S3 I5 K o7 f {+ a# V6 {, l! x* g DQ_OUT_LOW(); // Write 1# _9 u3 f% C/ K* g: B- Y delay_us(2);& ]+ T2 d, Y* P$ ^6 p DQ_OUT_HIGH();& y' @9 m* E C) s delay_us(60);% e; m7 p9 H# a$ w }! d: Z9 [; X" [* P/ k1 n else { DQ_OUT_LOW(); // Write 0 delay_us(60);* f! m8 k, b0 R- @3 l2 n7 ~ DQ_OUT_HIGH();: V" O; y( B- _; } delay_us(2);- |' l1 B2 k$ t( Z( w8 x9 g }' w+ }# Y+ R4 n. P+ ] }: v4 j. a" Z( @$ o9 ?- V6 |. s" i: ` } /* 函数名:DS18B20_Start$ p% B( b7 _. [1 |, T * 功能:开始温度转换* n0 \5 o& F t0 ]# z$ T$ ` * 输入:无 * 输出:无9 R) S7 E6 E! p * 备注:无: N9 r5 f: d7 W, s, g* { @9 H */ //) T6 W' ], T) @9 E' ~! U) y) u void DS18B20_Start(void)+ S6 W ~# @2 j; I$ p0 R7 {. X { DS18B20_Rst();6 k9 U- ]( m: j; g DS18B20_Check();6 O6 m) \; f# Q8 D9 z% I6 u# g DS18B20_Write_Byte(0xcc);// skip rom DS18B20_Write_Byte(0x44);// convert }8 ?- L/ v0 N- C8 K /* 函数名:DS18B20_Start * 功能:从 ds18b20 得到温度值 精度:0.1C7 E0 f0 S+ t/ @$ n3 k * 输入:无* G/ s; \! `3 L6 ` * 输出:返回值:温度值 (-55.0~125.0) * 备注:无# U# w3 s3 ], z- X& @ */- u# _1 q* ]0 G# ] short DS18B20_Get_Temp(void) { uint8_t temp;" d& V7 P4 q2 y" x& k4 ?" _+ { uint8_t TL,TH;* T0 S* J4 T# H' e- U short tem;DS18B20_Start ();' e! }9 R4 C# J3 f( M% m! Z // ds1820 start convertDS18B20_Rst(); DS18B20_Check(); DS18B20_Write_Byte(0xcc);// skip rom- G& r' K) q1 m; X DS18B20_Write_Byte(0xbe);// convert% _ c q: G2 [ TL=DS18B20_Read_Byte();8 i/ |; c' @- E, p( |$ g# o // LSB TH=DS18B20_Read_Byte();3 [% [3 j# } }2 w // MSB if(TH>7) {0 M8 [! t' P6 p: \8 u: @. f7 D TH=~TH;) j" {9 L i2 s& F( J! ` TL=~TL;' S) J& G) q! N1 Q2 S7 T8 \ temp=0;, N4 u- r2 B4 [3 X //温度为负 }else temp=1;9 ?" v7 T( Y/ L/ E$ _ //温度为正 tem=TH; //获得高八位 tem<<=8;tem+=TL; //获得底八位- z) N( _- Q( g+ E. l9 b tem=(float)tem*0.625; //转换 char buf[20]=""; if(temp)snprintf(buf,10,"Temp:%.1f",tem/10.0); else snprintf(buf,10,"Temp:%.1f",tem/10.0); LCD_PutString(10, 10, buf, White,Blue,1);% B5 S# s* I* D9 ~7 [" S- n4 i if(temp)return tem; //返回温度值5 o' {# M. `6 ~8 J/ x2 s! A else return -tem; } 读取 DHT11 核心代码:% ^9 E' o. T$ J- | /* 函数名:delay_us * 功能:微秒延时 * 输入:delay 延时多少微秒, ]) O- r0 h0 d: p% g2 [3 R7 g8 K * 输出:无 * 备注:无 *// b( B* w8 |8 K" j/ Z; s #define CPU_FREQUENCY_MHZ 170% j+ Q! x; B; S5 P5 k6 b // STM32 时钟主频+ f3 A# d! H, n3 m void delay_us(__IO uint32_t delay)4 G/ _6 a& r. M" s [+ u) I) z1 U {; W/ |; u% w; R8 c3 b9 Q int last, curr, val;" `( C& |6 D4 t# i o int temp; while (delay != 0) {0 w/ C4 u1 Z5 a5 T6 H temp = delay > 900 ? 900 : delay; last = SysTick->VAL; curr = last - CPU_FREQUENCY_MHZ * temp;. ^5 q& X7 w; t if (curr >= 0) { do { val = SysTick->VAL;& ~- G" \. z, i; T }' r1 N! z4 w9 ?8 X while ((val < last) && (val >= curr));' t$ g. |1 }: S }+ X, u2 ^$ X" E else' t1 I: b A6 {, e5 { {. ~3 A8 ]$ t. ?9 Y; r curr += CPU_FREQUENCY_MHZ * 1000; do {* J0 B( ~) \: V, v7 K0 C val = SysTick->VAL; } while ((val <= last) || (val > curr));0 u$ a% j' ^) K+ b; o- k' ]7 b% N } delay -= temp;+ q& P! t) ?1 w# i& v } }! m0 e3 w2 V' j+ G n% [; f( n unsigned int rec_data[4];) R0 c! n+ F! t7 o /* 函数名:DH11_GPIO_Init_OUT: T9 ^+ \, }# ^) \, l * 功能:初始化 DHT11 的 GPIO 为输出模式 * 输入:无 * 输出:无. }* a# |+ p' Z) U) v' \ * 备注:无 */6 ?+ C% Z0 \5 \( L void DH11_GPIO_Init_OUT(void). j3 x4 D, s1 X' [- D {' T) o3 t' z, U. M: F" y( q GPIO_InitTypeDef GPIO_InitStruct = {0};( ?6 W$ L3 X c1 A; h. {& U1 U GPIO_InitStruct.Pin = GPIO_PIN_9;7 N' q5 N& {% l' |" E7 o GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP; GPIO_InitStruct.Pull = GPIO_NOPULL; GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;7 I' V5 w6 W. \! Z* e/ y; Z9 e8 u$ l HAL_GPIO_Init(GPIOD, &GPIO_InitStruct); } // 对于 M0 来说,是输入 /* 函数名:DH11_GPIO_Init_IN4 }+ f, K! j3 U; T4 `& B6 h * 功能:初始化 DHT11 的 GPIO 为输入模式8 T* Z1 _! H2 B- m4 ^ * 输入:无) R3 R% u& C, ~# R- v# {/ { * 输出:无: l3 f0 @3 |. v7 q * 备注:无 */& ^( n7 o+ h% e2 ]& H* g void DH11_GPIO_Init_IN(void) {( W5 {% m( ^- @# h# ~, O. a GPIO_InitTypeDef GPIO_InitStruct = {0};5 k2 J0 L9 G6 _6 Q0 n& H GPIO_InitStruct.Pin = GPIO_PIN_9;$ b4 s, [. r' Z1 @; ^2 L9 o+ o GPIO_InitStruct.Mode = GPIO_MODE_INPUT;( k1 K% X `7 T1 j GPIO_InitStruct.Pull = GPIO_NOPULL; GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;4 r3 B! D) B3 @5 ^ HAL_GPIO_Init(GPIOD, &GPIO_InitStruct);: x2 i# u- r5 b2 X7 {, r // DL_GPIO_initDigitalInput(GPIO_DQ_dht11_and_ds18b20_IOMUX);//配置为上拉输入* L9 m6 ^4 h4 M" Y4 C } // 主机发送开始信号 /* 函数名:DHT11_Start N# i9 k9 m/ J5 v" [5 \+ o7 Z. ^ * 功能:主机发送开始信号/ n& ~/ \) |$ l8 S1 y9 [3 G6 F * 输入:无 * 输出:无 * 备注:无 */ void DHT11_Start(void), n& O( y% K7 v8 p9 F1 r6 e; S {+ f% B: v* p( ~/ r; W" f/ F ~ DH11_GPIO_Init_OUT(); // 输出模式 dht11_high; // 先拉高 delay_us(30); dht11_low; // 拉低电平至少 18ms8 F' q/ X3 Z2 @ HAL_Delay(20);" t$ [: |7 z+ g! h# C dht11_high; // 拉高电平 20~40us DH11_GPIO_Init_IN(); // 输入模式 delay_us(30); } // 获取一个字节 /* 函数名:DHT11_Rec_Byte& u1 G* x% R$ g% `$ S * 功能:获取一个字节 * 输入:无6 ?: H; X9 W; I2 w- o * 输出:读取的字符4 @) g# X. L7 v9 A * 备注:无 */, M2 u# j1 r. b: ? char DHT11_Rec_Byte(void) {; p3 f# }9 t6 P* v* p! X+ w a+ {5 \& d unsigned char i = 0; unsigned char data;& q: i& e4 ^3 V3 E for (i = 0; i < 8; i++) // 1 个数据就是 1 个字节 byte,1 个字节 byte 有 8 位 bit {( y- v& `6 z7 c [ while (dht11_read == 0)& Y# Z3 F! ?# g: s9 R ; // 从 1bit 开始,低电平变高电平,等待低电平结束 delay_us(30); // 延迟 30us 是为了区别数据 0 和数据 1,0 只有 26~28us3 x5 \$ a+ `, E: W& ]: ^/ } data <<= 1; // 左移 if (dht11_read == GPIO_PIN_SET) // 如果过了 30us 还是高电平的话就是数据 1; n, P* g3 z, w( P9 d {: v2 u& v0 y) D; q4 Z4 w! k data |= 1; // 数据+1 }; t% e! ?6 I# _4 Q+ n, |+ e1 R while (dht11_read == GPIO_PIN_SET) ; // 高电平变低电平,等待高电平结束! C% E* ^3 y: ^- s7 m" F0 a& { }6 I+ }# m, F6 h" z' ]& e" k$ n return data; } // 获取数据7 M* t. y ]+ M: E% v* t /* 函数名:DHT11_REC_Data {2 X; v0 g, U' A1 d * 功能:获取 DHT11 数据并打印 * 输入:无 * 输出:无3 @+ i. o6 @; L- s( a * 备注:无6 z- A5 g e+ Q( z: L */2 e+ Y$ j: m T, ]( S$ x: C4 t void DHT11_REC_Data(void)$ d5 L* |; l3 p) Y0 R! h { unsigned int R_H, R_L, T_H, T_L;" }# B8 R4 Y' z unsigned char RH, RL, TH, TL, CHECK;. M% Q; T0 b4 W8 y7 |' X' \ DHT11_Start(); // 主机发送信号+ R, X9 L" \; S //dht11_high; // 拉高电平- }5 S; }* s$ p* q if (dht11_read == 0) // 判断 DHT11 是否响应 { while (dht11_read == 0); // 低电平变高电平,等待低电平结束 while (dht11_read == GPIO_PIN_SET); // 高电平变低电平,等待高电平结束' o! s6 D5 p N3 |+ K$ V4 n R_H = DHT11_Rec_Byte(); R_L = DHT11_Rec_Byte();: {3 Z' |1 b/ B3 g6 T+ e& q, j# j9 u T_H = DHT11_Rec_Byte();( C* F: T7 @6 L* @+ f6 O T_L = DHT11_Rec_Byte();. D" N! H2 Z8 _& b CHECK = DHT11_Rec_Byte(); // 接收 5 个数据 //DH11_GPIO_Init_OUT();1 P# ?# W9 T+ F* U7 O# L- K //dht11_low; // 当最后一 bit 数据传送完毕后,DHT11 拉低总线 50us0 `, _0 j0 `* |5 d' }' E! h delay_us(55); // 这里延时 55us //dht11_high; // 随后总线由上拉电阻拉高进入空闲状态。 if (R_H + R_L + T_H + T_L == CHECK) // 和检验位对比,判断校验接收到的数据是否正确3 j3 l* |6 w) z& b( I1 { {; e2 W8 O- i# B7 I# s5 Q% \$ R! ~ f RH = R_H;2 B1 Y; i9 x' u4 L5 C$ }, L! I6 y RL = R_L; TH = T_H;$ \7 [' _( ]4 ]8 H2 T2 N TL = T_L;% Z( d& k' B7 q; I4 o3 o, J: F } }9 |# X5 t8 d2 e2 x. p. Z8 A$ r/ B/ E ' Z+ r6 C) k% |9 h0 s/ W$ K9 U3 d rec_data[0] = RH;' c% n; x. _. x0 z7 [: R rec_data[1] = RL; rec_data[2] = TH; rec_data[3] = TL;( J, T; P9 t6 F. G4 B# ] char buf[20]=""; snprintf(buf,10,"Temp:%d.%d",TH,TL);, t7 f6 d8 l$ O" W+ ?& @ LCD_PutString(10, 10, buf, White,Blue,1);. B& F, q0 I$ x o% B9 j3 M snprintf(buf,10,"Hum:%d.%d",RH,RL);- e) n+ m. W& \& V& _ LCD_PutString(10, 30, buf, White,Blue,1);}3 v. s4 G3 m: b 1 P' Z' r: f& T( J, T 主要代码: while(1){ DHT11_REC_Data();//DHT11 读取8 S; p: U$ }: w, J" ?: J. ?0 R //DS18B20_Get_Temp();//18B20 读取8 {. K: x4 M5 m. x0 q8 u HAL_Delay(1000);1 e# Z% h! C) Q# I8 f$ j% R } ( J L' K1 v! Z7 H$ T) W$ t: W3 p 实验现象: 下载烧录后可以观察到 LCD 显示 DS18B20 测试的温度,或者 DHT11 测量的温度和湿度。% V! x1 h1 M, P4 L' M/ k- G' }
▲ 图 3.12.2 实验现象 第十三、独立看门狗 实验目的:掌握和熟悉独立看门狗用法,包括喂狗操作等。2 y* I& Z/ @& f. \4 d4 M0 L6 O CubeMX 配置如下,保存后生成对应的配置代码:
▲ 图 3.13.1 CubeMX 进行独立看门狗配置 本实验进行独立看门狗配置。使用 CUBEMX 配置 IO 为输出输入模式实现 LED 指示和按键读取,GPIO 配置参考上文。程序中使用按键喂狗,如果没在 1s 内喂狗,系统将会自动复位。0 z: q6 P! T$ S4 L1 j H6 N/ y4 W 主要代码: while (1)% ^ N* M) @9 Q: f { if(HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_4)==0)8 }" f1 X" }1 `" W1 `) q { HAL_Delay(5);1 K& B8 F a: i9 T g if(HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_4)==0) {* r: L( y. l( I. D* _; H HAL_IWDG_Refresh(&hiwdg); HAL_GPIO_TogglePin(GPIOD,GPIO_PIN_10); while(!HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_4)); }& ]) d# q2 x9 j L) ?5 g2 W! D } /* USER CODE END WHILE */" g! ?2 v+ C: |; P /* USER CODE BEGIN 3 */- p; a- g) j5 Y! Q } 实验现象: 下载烧录后可以观察到如果在一定时间内进行喂狗操作,系统会复位,LCD 重新加载。6 m1 ~6 i0 E7 p
▲ 图 3.13.2 实验现象( }( x1 A0 C: c" [& O, g# ? $ c$ r o/ H6 C T 转载自: AI电堂 如有侵权请联系删除3 L4 |% Y9 ?3 X( ~1 [( _ V! I 6 U. D7 F) C# |0 o% J, a ' X8 q* p9 m$ \' t 2 N$ I2 K$ F. m# v |
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大神笔记,超详细单片机学习汇总资料
微信公众号
手机版