|
第十、COMP 实验( e5 g) W4 p- v& ]( w' J# J 实验目的:掌握和熟悉 G474 内部的模拟比较器用法,包括触发方式以及与内部 DAC 级联使用等。 1、软件读取 COMP 结果实验 CubeMX 配置如下,保存后生成对应的配置代码:
▲ CubeMX 进行 COMP 配置3 j; ^/ K# o# Z& M7 ] ]/ T 本实验使用软件读取 COMP 比较结果,不需要配置触发,为了使比较结果更加直观,开启外部比较结果输出。 Y, N! M5 ?4 E# n, U0 o( w . \( b1 }6 p/ n6 X* F/ a 相关操作函数说明:2 g) T5 i3 O& f, n) [6 e1 M5 ] HAL_StatusTypeDef HAL_COMP_Start(COMP_HandleTypeDef *hcomp), Z4 I! z+ w+ L) `# l" u 功能:开启比较器;! V. g9 s+ v9 \! m9 P7 b6 M& b 参数 1:比较器句柄,根据需要填写;: P1 o* y- N% J+ J1 _) k 返回:操作结果,HAL_OK 或 HAL_ERROR;% O6 | X0 g6 L0 e 示例:HAL_COMP_Start(&hcomp3);// 开启 COMP3HAL_StatusTypeDef HAL_COMP_Stop(COMP_HandleTypeDef *hcomp)0 @1 Z" r! C/ d% @' ~ 功能:关闭比较器; 参数 1:比较器句柄,根据需要填写; 返回:操作结果,HAL_OK 或 HAL_ERROR;. O! d$ L3 _. `/ v( h% T. z uint32_t HAL_COMP_GetOutputLevel(const COMP_HandleTypeDef *hcomp) 功能:读取比较器输出电平; 参数 1:比较器句柄,根据需要填写;% \0 T( z$ U5 e2 o% r ~+ D 返回:比较结果,COMP_OUTPUT_LEVEL_LOW 或 COMP_OUTPUT_LEVEL_HIGH;: H2 `! ?3 b3 Z4 ~, n 示例:result = HAL_COMP_GetOutputLevel(&hcomp3); //软件读取比较结果! x/ D, W* C( V/ x' Y+ h 2 D: f$ T2 l3 ]/ ~$ g$ [ 核心代码: if(HAL_COMP_Start(&hcomp3) != HAL_OK) //开启比较器- ?2 d8 h9 I% U; Y {7 H* g5 j. N5 o4 |6 u% M Error_Handler();- e5 f# a4 {" `6 o. Z }" e+ B+ S5 C0 o8 w+ s7 \ while (1) { result = HAL_COMP_GetOutputLevel(&hcomp3); //软件读取比较结果# Y# \$ k6 B# G* n if(result == COMP_OUTPUT_LEVEL_HIGH)//比较结果为 1,即 INP 大于 INM {& A: c w- s+ B3 F* P j HAL_GPIO_TogglePin(GPIOD,GPIO_PIN_11);//翻转 LED2 }; }% D) d. N2 Y* X) D9 C HAL_Delay(100);- S8 S2 h7 r' F7 I9 Q* S3 P0 z }0 z* V& N( {. o, I5 } ; ^6 ?, X* L) n1 e# F 以上为 main 函数中外设初始化结束后的部分首先开启比较器,然后在主循环中每 100ms读取一次比较结果,如果结果为 1,即正相输入大于反相输入,则进行 LED2 翻转。% J! U! V, o/ s7 T" m 实验现象: 下载烧录后可以观察到拨动拨盘电位器,或者遮蔽光敏电阻时可以改变比较结果,CMP 亮时,LED2 状态不变,CMP 灭时,LED2 闪烁。 - t4 q4 c8 p$ y) |3 \4 W 2、中断读取 COMP 结果实验7 K1 D8 I& a$ z CubeMX 配置如下,保存后生成对应的配置代码:
4 x, a1 X; M4 S% \ ▲ CubeMX 进行 COMP 配置# D6 L' e0 c- X& u2 g/ ^ 6 g9 }5 F6 x5 k- ^# f5 x, w; R 本实验使用中断读取 COMP 比较结果,在比较结果变化的上升沿和下降沿都触发中断,为了使比较结果更加直观,开启外部比较结果输出。 ' c9 A" h7 t& Z+ Z0 X+ W 核心代码: if(HAL_COMP_Start(&hcomp3) != HAL_OK) //开启比较器# U* W0 ? V6 w7 q- i {$ D% q) Y' X- x0 U6 \ Error_Handler(); }/ l# Z% U- b; i) N' I# W+ b 以上为 main 函数中外设初始化结束后的部分,这里只需要开启比较器即可,使用函数与上例相同。) u5 P0 P3 Y/ ]! d8 f ) f6 G l3 G* _- ^ ; {; c4 _' k e( Z$ K- q void HAL_COMP_TriggerCallback(COMP_HandleTypeDef* hcomp) {: n) d% W& T8 d: W) p1 f5 j; M% T2 V+ z if(hcomp->Instance==COMP3) { uint8_t temp;1 K! ]4 d9 [% }) t$ s8 {3 J* r/ E temp = HAL_COMP_GetOutputLevel(&hcomp3);//读取比较结果 if(temp == COMP_OUTPUT_LEVEL_HIGH)//结果为 1,上升沿触发 { HAL_GPIO_WritePin(GPIOD,GPIO_PIN_10,GPIO_PIN_RESET);//点亮 LED1( [ R, ]; l# Z9 L2 x2 k$ e% \# M HAL_GPIO_WritePin(GPIOD,GPIO_PIN_11,GPIO_PIN_SET);//熄灭 LED20 i/ j& J" S3 A/ f5 n0 [% A5 A2 [ }. m* n( L& M$ q. v- @ else4 G3 R& y7 m- o: m5 o { HAL_GPIO_WritePin(GPIOD,GPIO_PIN_10,GPIO_PIN_SET);//熄灭 LED1 N7 D4 c% { N" h HAL_GPIO_WritePin(GPIOD,GPIO_PIN_11,GPIO_PIN_RESET);//点亮 LED26 a, _. h# z$ F- N( r }( p2 q. V0 k- r7 [ }+ g) k' y. |! i. L } 3 z% Z6 C: M- } 以上为 COMP 触发中断回调函数,该回调函数为比较器共用,需要判断中断源,如果开启了多个边沿,还需要判断具体边沿。实质上,COMP 中断触发与 EXTI 类似,实际就是将COMP 比较输出作为一个 IO 映射到 EXTI 进行中断。 " c K) u* K0 q; N+ h0 ? 实验现象: 下载烧录后可以观察到拨动拨盘电位器,或者遮蔽光敏电阻时可以改变比较结果,CMP 亮时,LED1 灭,LED2 亮,CMP 灭时,LED1 亮,LED2 灭。 3、内部 DAC 级联比较实验 CubeMX 配置如下,保存后生成对应的配置代码:0 u1 s, C, A: c$ f6 V" m. q* c
▲ CubeMX 进行 COMP 配置0 ~2 ?: E5 v0 p& @
▲ CubeMX 进行 DAC 配置 9 w9 ~, m$ R" R4 H- T1 _ 本实验使用软件读取 COMP 比较结果,正相输入为光敏电阻,反相输入使用 DAC3 的 OUT1作为比较电压,为了使比较结果更加直观,开启外部比较结果输出。, n+ T- Z& S7 U5 A4 N! Z3 L: j7 g 核心代码:6 J, S3 a V7 b8 k void DAC3_CH1_Set_Vol(uint16_t vol)$ T- `4 ~& \7 [4 O {6 I1 n! ?: o% N# b: ^ double temp=vol; temp/=1000;. |$ N! g4 j3 G7 H) R. {4 p4 c: | temp=temp*4096/3.3; HAL_DAC_SetValue(&hdac3,DAC_CHANNEL_1,DAC_ALIGN_12B_R,temp);//12 位右对齐数据格式设置 DAC 值; L5 a5 V2 W# K5 ^ } 3 ~/ I/ J0 w5 O 1 ^8 \+ v% Z9 S- E8 g$ {5 @" @# P( g 以上为 DAC 设置函数,通过该函数可以将输入的电压快速转换为 DAC 所需的寄存器数据。- r( d: T3 u; ?& h! ?5 _ DAC3_CH1_Set_Vol(500);//DAC 输出设置为 500mv HAL_DAC_Start(&hdac3,DAC_CHANNEL_1);//生效 DAC 输出 if(HAL_COMP_Start(&hcomp3) != HAL_OK)# h# V5 e* s r0 T% w //开启比较器 {% f, I N2 z$ |' r Error_Handler();: W4 [% b5 @% ~" ]' q } while (1) x6 i5 T! d' U+ X- p { result = HAL_COMP_GetOutputLevel(&hcomp3); //软件读取比较结果; @8 e6 ]; t! K" L( ? if(result == COMP_OUTPUT_LEVEL_HIGH)//比较结果为 1,即 INP 大于 INM2 \1 B. q+ U A) S# S {! x6 d: s" T6 R! Y. p HAL_GPIO_TogglePin(GPIOD,GPIO_PIN_11);//翻转 LED2/ c. }8 g4 l/ \" m+ {) e) d }. f# h3 _1 u) p HAL_Delay(100); }' W9 S. u7 T: T9 r* H 以上为 main 函数中外设初始化结束后的部分,首先设置好 DAC 输出电压,开启 DAC,然后开启比较器,最后在主循环中每 100ms 读取一次比较结果,如果结果为 1,即正相输入大于反相输入,则进行 LED2 翻转。 实验现象: 下载烧录后可以观察到遮蔽光敏电阻时可以改变比较结果,CMP 亮时,LED2 状态不变,CMP 灭时,LED2 闪烁。0 O& V% j4 J, N9 w9 ~ 第十一、FLASH 实验 实验目的:掌握和熟悉 G474 内部的 FLASH 用法,包括 FLASH 读写应用等。FLASH 读取无需进行配置。 5 c) E) _8 Z: \4 _( v! U' l 核心代码: /* 函数名:FLASH_Write- K& _" }6 }% Y3 U a% Y L * 描述 :flash 写入数据 * 输入 :data 写入数据地址 addr flash 地址 size 写入字节数' f3 ~% S. a7 @8 K * 输出 :无 * 调用 :FLASH_Write(data,FLASH_PAGE_127,8);//8 字节 */ uint8_t FLASH_Write(uint8_t* data,uint32_t addr,uint16_t SIZE) {4 a% o5 Y! U1 M FLASH_EraseInitTypeDef EraseInitStruct;//定义擦写操作结构体8 i7 e r5 H7 n* T: O6 `! } uint32_t SECTORError = 0; uint64_t write_buff[10];//写入缓冲数组0 c4 j) B) b% E" s3 D) J1 U x' C; } uint8_t i = 0;% @0 x1 T: m2 g$ }) a' @ uint8_t size = SIZE/8;//8 字节写入次数 memcpy(write_buff,data,SIZE);//将输入的数组移动到写入缓冲区 HAL_FLASH_Unlock();//解锁. N! K/ |+ G0 J% R+ N m0 T EraseInitStruct.Banks = FLASH_BANK_1; //存储区 1 EraseInitStruct.TypeErase = FLASH_TYPEERASE_PAGES;//页擦除 EraseInitStruct.Page = (addr-0x08000000)/FLASH_PAGE_SIZE;//擦除页; H5 s5 O' T5 X4 L9 f. k! I EraseInitStruct.NbPages = 1;//擦除页数. b x. j, ^/ p' k. T( Q if(HAL_FLASHEx_Erase(&EraseInitStruct,&SECTORError) != HAL_OK) //擦除页. z8 \7 Z% m, K" f2 ~1 C {4 ~# g& M5 Z1 \ return 1;//擦除失败: i! G1 _# y/ N* p6 p& a }. H! N% T1 D) l' ]7 b( w9 w, O while(size) {( @, D5 k7 C" t& G2 I1 d if(HAL_FLASH_Program(FLASH_TYPEPROGRAM_DOUBLEWORD,addr,write_buff)!= HAL_OK)//双字写入 { return 2;//写入失败 } b. c+ R# L6 Y) R; M addr = addr+8;% {) n9 U3 w& ?2 z* U6 Y i++;/ D; O6 t1 F" m% h4 q2 K- G size--;6 {" s$ t; x s }1 l* L5 _7 R2 A5 i) \ HAL_FLASH_Lock(); //锁定 FLASH return 0; }; ?+ w9 Q! F' U6 c S6 E /* 函数名:FLASH_Read * 描述 :flash 读取数据# ?- }- P6 k' l0 {6 E1 p) Z" h, K1 i * 输入 :data 读取数据地址 addr flash 地址 size 读取字节数+ T$ I! B1 V; @& V* ^ * 输出 :无 * 调用 :FLASH_Read(data,FLASH_PAGE_127,8);//8 字节( j; @5 k: J: m/ R* v& \' | */ uint8_t FLASH_Read(uint8_t* data,uint32_t addr,uint16_t SIZE) {' L; l. T* O, I; I3 c! ] uint32_t read_buff[10]; //接收缓冲数组 uint8_t i = 0; uint16_t size = SIZE/4; //接收次数 while(size)+ H: ]" r, l+ c& J. y3 { { \( s% w/ z' y# Y read_buff = *(__IO uint32_t*)addr; //从 FLASH 读取数据到接收缓冲数组/ n- }/ I- T* c( h( ^/ r addr = addr+4; i++;6 H9 i$ m6 U( |6 R y. F5 D size--;/ P' P8 a( O& A- ? } memcpy(data,read_buff,SIZE); //将数据从接收缓冲数组转移到接收数组 return 0;; m; H& D5 D! O. I9 k P }3 u: e; c# W. I2 H' o8 [ 8 F2 j0 `( b9 o8 |4 @ 主要代码:# W! y- ^" _- [# z+ n, G uint8_t data[8]={0,0,0,0,0,0,0,0}; char buf[30]=""; FLASH_Read(data,FLASH_PAGE_127,8);//8 字节5 D2 t0 g$ N1 H! r data[0]++;FLASH_Write(data,FLASH_PAGE_127,8);//8 字节( d) L2 u' c6 v snprintf(buf,10,"times:%d",data[0]); LCD_PutString(10,30,buf,Red,White,0);, y0 Z& B1 O, \1 c* X uint32_t UID1=READ_REG(*((uint32_t *)UID_BASE)); uint32_t UID2=READ_REG(*((uint32_t *)(UID_BASE+4U))); uint32_t UID3=READ_REG(*((uint32_t *)(UID_BASE+8U))); snprintf(buf,30,"UID:%0x-%0x-%0x",UID1,UID2,UID3);8 ^4 b# @- z( \& I LCD_PutString(10,60,buf,Red,White,0); 实验现象:/ k. V3 p8 y' q+ w- F 下载烧录后可以观察到 LCD 显示 FLASH 测试次数以及芯片的 ID。
▲ 实验现象 ( Y8 _$ H4 c6 x. `: w % Y( T% @8 l% j! i6 i( K- [ 第十二、单总线实验 实验目的:掌握和熟悉常用的单总线通信,包括 DS18B20,DHT11 读写应用。- w+ r* R2 S8 A4 B CubeMX 配置如下,保存后生成对应的配置代码:7 A# P) S+ P6 @) m9 J
▲ 图 3.12.1 CubeMX 进行 GPIO 输出配置" d% {/ l4 y( @ 7 y' F! d+ Y* z9 [ 本实验进行单总线读取 DS18B20 和 DHT11。使用 CUBEMX 配置 IO 为输出模式。" z& }8 u; z% j5 A& B 读取 DS18B20 核心代码:1 y0 Z2 g& @1 q( t; E) I* t /* 函数名:DS18B20_IO_OUT * 功能:初始化 DS18B20 的 GPIO 为输出模式9 C1 F' Y# K; r4 Z1 u * 输入:无/ k2 b, A. c2 T" l) u3 V * 输出:无6 X: v: G+ r2 h$ G9 v3 t9 l9 U * 备注:无: ~1 l. A- S* E" }* W- ? */+ `3 u5 u$ G. u2 ~ void DS18B20_IO_OUT(void ) { GPIO_InitTypeDef GPIO_InitStruct = {0};# O; e. n, A" O7 a4 p' ` GPIO_InitStruct.Pin = GPIO_PIN_9; GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;6 x0 w5 I% R, `. Z- {! c# J' k/ E GPIO_InitStruct.Pull = GPIO_NOPULL;! K/ ~* k* y5 r4 d" X+ t GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;5 q5 D2 z; E7 S4 z V7 w7 p6 m7 H HAL_GPIO_Init(GPIOD, &GPIO_InitStruct); h7 I0 j& O; x* d! `" w& q } /* 函数名:DS18B20_IO_OUT * 功能:初始化 DS18B20 的 GPIO 为输入模式 * 输入:无 * 输出:无9 O3 f# X% |& U' X * 备注:无 */void DS18B20_IO_IN(void )8 N8 e5 f, m' V& H { GPIO_InitTypeDef GPIO_InitStruct = {0}; GPIO_InitStruct.Pin = GPIO_PIN_9; GPIO_InitStruct.Mode = GPIO_MODE_INPUT; GPIO_InitStruct.Pull = GPIO_NOPULL; GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;/ p9 y2 y$ d* n4 p/ E$ b* \7 n/ Q HAL_GPIO_Init(GPIOD, &GPIO_InitStruct); } /* 函数名:DS18B20_Rst7 W. D% ^9 j0 Y# H! J2 m * 功能:复位 DS18B20 * 输入:无 * 输出:无 * 备注:无# x1 M2 n9 R9 e7 v- n% S( k */ void DS18B20_Rst(void). _" w e& s. i" V! n: ] {: M) v [, X& _ [ DS18B20_IO_OUT(); //SET PG11 OUTPUT4 [/ s% F9 v& _7 u% g1 K DQ_OUT_LOW(); //拉低 DQ, j" T, H# e8 s, V1 j$ Z0 G delay_us(750);/ ?* M2 r' n% k' u2 X6 c //拉低 750us DQ_OUT_HIGH();6 u( H) s7 o' R/ T7 Q5 \! H1 \ //DQ=1- t: A( X3 t2 h! g }5 h delay_us(15); F6 m% W' A' M+ M5 `8 C //15US2 ~3 U7 Y+ K! M8 s, b# X } /* 函数名:DS18B20_Check * 功能:等待 DS18B20 的回应. }: ~' R9 j3 `' L3 ]' J3 t * 输入:无) F0 ?( H/ Y% p * 输出:返回 1:未检测到 DS18B20 的存在 返回 0:存在 * 备注:无0 b6 o- i: o" R1 M0 K */ uint8_t DS18B20_Check(void) { uint8_t retry=0;/ @( _6 w: R1 N) B) e& I$ Y" _" x DS18B20_IO_IN(); //SET PG11 INPUT while (DQ_GET_IN()&&retry<200)* M/ o9 A" o s {1 v# l( V4 e# b. G) W D( m. J retry++;delay_us(1); }; if(retry>=200)return 1; else retry=0;5 S! F d' M0 |9 L7 N8 T& l" Z while (!DQ_GET_IN()&&retry<240) { retry++; delay_us(1); }; if(retry>=240)return 1;) a O& w- d/ }' Z l+ a return 0; }7 a" k0 S7 ]" M) O& P& K' a /* 函数名:DS18B20_Read_Bit$ P3 ^8 N4 @& a- { * 功能:从 DS18B20 读取一个位 * 输入:无0 L. V4 U! x4 D8 ~ { * 输出:返回值:1/02 t/ W$ a s3 ?+ V5 a0 \ * 备注:无. U' k" Q" L! l c */ uint8_t DS18B20_Read_Bit(void)% E. D' l8 C0 S: f* f {0 V5 W9 n% T. W& f% F7 y uint8_t data; DS18B20_IO_OUT(); //SET PG11 OUTPUT DQ_OUT_LOW();delay_us(2); DQ_OUT_HIGH(); DS18B20_IO_IN(); //SET PG11 INPUT6 T& |* p( r+ T! s delay_us(12);4 f5 m8 |! ~* |* J- |# r. s& a/ S if(DQ_GET_IN())data=1;- a' i6 H* p7 R& I else data=0; delay_us(50);1 i3 Y1 p# B! {3 p* z2 r0 [) H return data; }1 H- b* S% n* q% Y; T /* 函数名:DS18B20_Read_Byte * 功能:从 DS18B20 读取一个字节 * 输入:无% C' B! ~7 L" r. J7 r# i: S# F0 ^ * 输出:返回值:读到的数据 * 备注:无$ ~& X. V; F X1 [! j9 G* r& X */5 y1 j# A- c' c7 w uint8_t DS18B20_Read_Byte(void)) a" o+ u' z! \4 a: Y {9 C- Y* y. Y) g; Y& L uint8_t i,j,dat;$ b* x" n/ ?: i( K1 D9 ^% | dat=0;: r/ w% N+ b, L5 S, p; w$ c) Q# u for (i=1;i<=8;i++)- b. H3 P: {! s6 W2 V) Q k. ? { j=DS18B20_Read_Bit(); dat=(j<<7)|(dat>>1); }, X& l' K3 {3 l7 P! e% L return dat;9 d: ^9 k2 S8 r- o: x/ L! i" z! a } /* 函数名:DS18B20_Write_Byte * 功能:写一个字节到 DS18B20* {4 | F, e' z% j% K7 T * 输入:dat:要写入的字节 * 输出:无 * 备注:无. D6 V4 ]. O% n( b/ j' b5 V */ void DS18B20_Write_Byte(uint8_t dat) { uint8_t j; uint8_t testb;- r7 N( K1 w3 \6 K7 k& ~ DS18B20_IO_OUT(); //SET PG11 OUTPUT;9 K0 Y* D. a# b" C6 ?+ X1 [ for (j=1;j<=8;j++) {5 y3 E- h @7 E testb=dat&0x01;# }; Y t* M- b9 D dat=dat>>1;& X' [. `3 ]+ c0 Z H1 T1 L/ B6 j if (testb) M6 s9 L- M7 D- w$ |; c { DQ_OUT_LOW(); // Write 1 delay_us(2); DQ_OUT_HIGH(); delay_us(60);) Z$ t" J# g3 u4 T' {: V, K9 [ }" p. {: R8 x9 s; x2 Z else' }9 A7 a& M# d4 i# p! | {" n( K0 r& m* @: Q2 y8 h, [. R. E DQ_OUT_LOW(); // Write 0 delay_us(60); DQ_OUT_HIGH(); delay_us(2); } } } /* 函数名:DS18B20_Start * 功能:开始温度转换- j: d u3 ?; ^ * 输入:无 * 输出:无 * 备注:无1 T5 g0 H5 l# L/ S) U: R */ // void DS18B20_Start(void) {( y- ^1 T8 x; H+ Z DS18B20_Rst(); DS18B20_Check();* E' U6 |- A* n9 q8 H4 A DS18B20_Write_Byte(0xcc);// skip rom" ]6 l% |9 c( q* L# N; T DS18B20_Write_Byte(0x44);// convert } /* 函数名:DS18B20_Start * 功能:从 ds18b20 得到温度值 精度:0.1C * 输入:无 * 输出:返回值:温度值 (-55.0~125.0) * 备注:无 */ short DS18B20_Get_Temp(void)! E5 Z! g0 }. M- z w3 v- Y { uint8_t temp;! e2 X4 g+ C5 c0 o/ P) B' ]) e1 H uint8_t TL,TH;5 i- i2 }4 ]# e0 L9 e- M# g short tem;DS18B20_Start (); // ds1820 start convertDS18B20_Rst(); DS18B20_Check();1 J% J" H, T- f$ n* P5 p& F DS18B20_Write_Byte(0xcc);// skip rom0 Z2 b# j. H3 k9 V' V, r/ G0 }+ M DS18B20_Write_Byte(0xbe);// convert* F( s1 e8 U: s5 j* ~' D" }" |" a TL=DS18B20_Read_Byte();" r& s9 \8 M5 t+ Y4 f1 _$ [ // LSB' O, O$ m* Y1 a- d' X8 g3 a" M TH=DS18B20_Read_Byte();( R9 m& ~- d2 o2 M: L* O& o5 } // MSB if(TH>7) {+ _" @5 F" _( v0 [( g/ Z, t TH=~TH; TL=~TL;& r1 _- H, { n7 h r0 x0 f, o temp=0;, v1 l7 ?% J4 b n2 t7 A //温度为负* x: z: \% q5 y0 } }else temp=1;: w: a; [* c- o3 L" B1 p //温度为正 tem=TH;6 ~: M, e$ d% O5 v! n7 B //获得高八位 tem<<=8;tem+=TL;% o: W$ L5 n k! A //获得底八位 tem=(float)tem*0.625;7 ~3 |2 p" S& y! i: s( s# k //转换( J8 b( B% |7 j4 |9 s) ?# t char buf[20]=""; if(temp)snprintf(buf,10,"Temp:%.1f",tem/10.0); else snprintf(buf,10,"Temp:%.1f",tem/10.0);! V, k+ t' H/ s, M* l2 V( z LCD_PutString(10, 10, buf, White,Blue,1); if(temp)return tem; //返回温度值' j) q% S) j0 {) e6 E9 ~ else return -tem; }3 p1 K* \ i- ]/ y 读取 DHT11 核心代码: /* 函数名:delay_us * 功能:微秒延时% \8 q% P, t+ L; o * 输入:delay 延时多少微秒. @7 P$ a4 ]3 O1 q, C * 输出:无/ H- r$ u$ z) T1 p * 备注:无 */ #define CPU_FREQUENCY_MHZ 170 // STM32 时钟主频" j9 J( V ~( ` void delay_us(__IO uint32_t delay): r) E4 I0 P8 I1 S. B7 ^, y { int last, curr, val; int temp; while (delay != 0) { temp = delay > 900 ? 900 : delay; last = SysTick->VAL;6 E; b# e& I+ N' N curr = last - CPU_FREQUENCY_MHZ * temp;( b3 R5 F6 n7 \/ v+ K ~ if (curr >= 0) { do { val = SysTick->VAL;' t# F9 J, A$ D }& X: e3 x5 `6 _$ D while ((val < last) && (val >= curr)); } else {7 _5 w- s. X) g* @ curr += CPU_FREQUENCY_MHZ * 1000;0 H+ t& ^( S2 H1 r( u- d5 H+ A do { val = SysTick->VAL;. p0 G% S& I+ P S }, u( [% `6 `* \5 i# j7 v3 V H while ((val <= last) || (val > curr));. Z( ^7 b* h! p+ N( j n } delay -= temp; }& X7 E6 ]3 @ V2 F2 r4 ~2 Y } unsigned int rec_data[4]; /* 函数名:DH11_GPIO_Init_OUT * 功能:初始化 DHT11 的 GPIO 为输出模式 * 输入:无 * 输出:无* T: T. t- A) h, q; ]2 s * 备注:无: P5 n9 n+ E0 `2 Y */5 Z0 H9 Y1 W9 s& x9 N- u3 |5 G* w void DH11_GPIO_Init_OUT(void)+ F7 @7 M; K! w0 | { GPIO_InitTypeDef GPIO_InitStruct = {0}; GPIO_InitStruct.Pin = GPIO_PIN_9;+ F3 ]# ?. e+ \1 D8 _* Q% L/ Z GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;* C; C, ~" |5 J9 g+ v. Q GPIO_InitStruct.Pull = GPIO_NOPULL;9 H. I- H0 x+ J9 i, k; u' ?' t GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW; HAL_GPIO_Init(GPIOD, &GPIO_InitStruct); }$ M" C' @+ l& I4 V- ?3 r // 对于 M0 来说,是输入) z. P" h( l# C( d /* 函数名:DH11_GPIO_Init_IN/ B; ], r. J9 h O$ C' n * 功能:初始化 DHT11 的 GPIO 为输入模式 * 输入:无0 ?" Z8 _7 ~2 M& _9 \$ d# h * 输出:无3 y, \2 u9 _! d9 U# A3 r) B2 C$ u * 备注:无 */% c$ R4 ]9 n; M void DH11_GPIO_Init_IN(void)% m2 w4 ]) k" ? { GPIO_InitTypeDef GPIO_InitStruct = {0}; GPIO_InitStruct.Pin = GPIO_PIN_9; GPIO_InitStruct.Mode = GPIO_MODE_INPUT;' x" v) k2 s% Z GPIO_InitStruct.Pull = GPIO_NOPULL;0 F( r6 p5 l6 o" M6 j6 T GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;/ B& j ^# \. w [3 u$ {4 s HAL_GPIO_Init(GPIOD, &GPIO_InitStruct);1 x" T6 ?/ q; f/ T // DL_GPIO_initDigitalInput(GPIO_DQ_dht11_and_ds18b20_IOMUX);//配置为上拉输入 V- M7 ~* c" e e: ~% W3 p( k } // 主机发送开始信号, r6 B+ Y- {+ l8 t5 ]8 P0 f8 i /* 函数名:DHT11_Start8 r/ V; @! @$ F5 ^# S1 |# \: T5 [ * 功能:主机发送开始信号 * 输入:无 * 输出:无 * 备注:无 */ f8 K+ G5 {. |! Z void DHT11_Start(void)- i4 n2 J7 V G7 p" d { DH11_GPIO_Init_OUT(); // 输出模式+ W, [( x! r9 J* R* ^ dht11_high; // 先拉高5 Y1 U0 L6 Q5 s, B0 ?- y delay_us(30); dht11_low; // 拉低电平至少 18ms7 V: `$ A6 W T7 f HAL_Delay(20); dht11_high; // 拉高电平 20~40us DH11_GPIO_Init_IN(); // 输入模式2 K/ T" Y; w2 U {, ] delay_us(30); }! @; U* y: c" P( L' k // 获取一个字节- I) ]; e/ y" ?0 x) g1 h /* 函数名:DHT11_Rec_Byte * 功能:获取一个字节% {0 c' U: ~% ] S0 [, L* _ * 输入:无 * 输出:读取的字符 * 备注:无# E# b4 Q- e; v *// m8 e8 q' E/ O: z char DHT11_Rec_Byte(void) { unsigned char i = 0;. n0 I; W$ p, K$ D9 ?: c2 ?$ | unsigned char data; for (i = 0; i < 8; i++) // 1 个数据就是 1 个字节 byte,1 个字节 byte 有 8 位 bit {, r. U6 B: l# \* C' D8 d5 p* { while (dht11_read == 0) ]( i5 |5 B/ X( A: ^: } ; // 从 1bit 开始,低电平变高电平,等待低电平结束 delay_us(30); // 延迟 30us 是为了区别数据 0 和数据 1,0 只有 26~28us; P; J" I: H, x data <<= 1; // 左移1 Q" @* F& O; E2 e1 K; P4 W if (dht11_read == GPIO_PIN_SET) // 如果过了 30us 还是高电平的话就是数据 1( G+ t3 `0 A1 I0 M0 c6 n* g { data |= 1; // 数据+1 } while (dht11_read == GPIO_PIN_SET)# g4 Y! H1 h3 L3 Y( o' w9 z( G- U ; // 高电平变低电平,等待高电平结束 }) e2 G! d9 x- M return data; } T# g J, }; w // 获取数据 /* 函数名:DHT11_REC_Data: D* O. x! j( f6 I7 w) g * 功能:获取 DHT11 数据并打印6 N6 W; \2 L* Q1 ` * 输入:无 J# ~% b5 h" t( h8 V) X * 输出:无 * 备注:无 */ void DHT11_REC_Data(void) { unsigned int R_H, R_L, T_H, T_L;1 W6 l0 G9 P. ~1 }. T# \: X unsigned char RH, RL, TH, TL, CHECK; DHT11_Start(); // 主机发送信号 //dht11_high; // 拉高电平$ H9 V$ h% j1 e' Q if (dht11_read == 0) // 判断 DHT11 是否响应 { while (dht11_read == 0); // 低电平变高电平,等待低电平结束 while (dht11_read == GPIO_PIN_SET); // 高电平变低电平,等待高电平结束 R_H = DHT11_Rec_Byte();- U% h2 c8 B! f; R R_L = DHT11_Rec_Byte();. b6 N1 b& G) O) |8 v. T T_H = DHT11_Rec_Byte(); T_L = DHT11_Rec_Byte();; t; l/ W/ O1 `' K; D A7 P U CHECK = DHT11_Rec_Byte(); // 接收 5 个数据' p8 @" ]5 A9 R9 l //DH11_GPIO_Init_OUT(); //dht11_low; // 当最后一 bit 数据传送完毕后,DHT11 拉低总线 50us delay_us(55); // 这里延时 55us //dht11_high; // 随后总线由上拉电阻拉高进入空闲状态。) ?0 [( H/ o# h% S4 x1 \1 |. r p if (R_H + R_L + T_H + T_L == CHECK) // 和检验位对比,判断校验接收到的数据是否正确 {7 H. Y& y3 o& l1 P, l RH = R_H;/ w6 o& T. E( F' r+ T RL = R_L;# h) Q/ z: d0 f2 x2 | TH = T_H; TL = T_L; f4 `6 S1 C m8 F8 a1 h } }& Y( @# Z* b$ n2 ] ! I0 `' T6 c3 A# C1 N' a2 S7 ]# L $ W( K' p! j n! t& g/ M rec_data[0] = RH;* Z' |# `0 J1 F2 p rec_data[1] = RL;$ \( I; a7 i4 d5 T rec_data[2] = TH; Y2 O+ x0 ? k' q rec_data[3] = TL;7 n5 {1 ]% w# M1 d char buf[20]=""; snprintf(buf,10,"Temp:%d.%d",TH,TL); LCD_PutString(10, 10, buf, White,Blue,1);9 Y* S: d/ q- v9 m2 D snprintf(buf,10,"Hum:%d.%d",RH,RL); LCD_PutString(10, 30, buf, White,Blue,1);}8 L5 G& N) x% K& N, Q5 Y6 b: h ' R9 T2 K+ W4 Q7 v8 N M0 l 主要代码: while(1){ DHT11_REC_Data();//DHT11 读取$ g3 ]/ d$ W8 ^9 e p$ c //DS18B20_Get_Temp();//18B20 读取 HAL_Delay(1000); }( E8 Q. s6 h$ J' ]: T8 N9 { 4 N7 [+ [% {1 m6 ^ , Z" q! l; K7 n7 W 实验现象:4 f, f" N8 `) o 下载烧录后可以观察到 LCD 显示 DS18B20 测试的温度,或者 DHT11 测量的温度和湿度。
$ d3 [% B- @. Y' G) |* c9 ^ ▲ 图 3.12.2 实验现象) B5 y! i& s) j, ?$ K6 i2 W 0 |. f+ o! T* ? 第十三、独立看门狗 实验目的:掌握和熟悉独立看门狗用法,包括喂狗操作等。1 q) d9 W0 M; H+ C* l: T- {9 q d8 j CubeMX 配置如下,保存后生成对应的配置代码:! N9 |9 t. u+ W: u m
▲ 图 3.13.1 CubeMX 进行独立看门狗配置; G1 Z, Z: J2 g! S5 D0 ^ . k* @2 C% c8 W 本实验进行独立看门狗配置。使用 CUBEMX 配置 IO 为输出输入模式实现 LED 指示和按键读取,GPIO 配置参考上文。程序中使用按键喂狗,如果没在 1s 内喂狗,系统将会自动复位。 主要代码: while (1) {' @0 E4 `1 E$ h( I9 K if(HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_4)==0). E7 h3 l% P! q* s9 f$ E0 u- c { HAL_Delay(5); if(HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_4)==0) {% G* c: K. l# q# p4 R8 _ HAL_IWDG_Refresh(&hiwdg);3 Z3 x' v; R1 Y% |8 m HAL_GPIO_TogglePin(GPIOD,GPIO_PIN_10);' J" |" F4 r& k8 i$ t1 j while(!HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_4)); } }! T+ z& O9 |* g) j9 u /* USER CODE END WHILE */# H/ `6 {8 u! q- E6 U0 b8 j /* USER CODE BEGIN 3 */ } 8 |- ~) u% m0 O. u; L+ X 实验现象:% ?! ^& _1 A H, g. A 下载烧录后可以观察到如果在一定时间内进行喂狗操作,系统会复位,LCD 重新加载。
▲ 图 3.13.2 实验现象 转载自: AI电堂 如有侵权请联系删除 % I" j+ r% p# X g9 { ' w, p `; d) e4 Q5 [) K+ \# p |
经验分享 | 基于STM32G4芯片TIM+SPI+DMA应用示例
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°相位同步方案
微信公众号
手机版