|
第十、COMP 实验 实验目的:掌握和熟悉 G474 内部的模拟比较器用法,包括触发方式以及与内部 DAC 级联使用等。. L* h) B, t' Z e& G " P. B+ i( @' b3 N6 `/ l6 Y 1、软件读取 COMP 结果实验 CubeMX 配置如下,保存后生成对应的配置代码:
9 E. z1 ]0 _ y8 R. G$ \6 s ▲ CubeMX 进行 COMP 配置' {& |2 U/ `# Q9 Z. t 本实验使用软件读取 COMP 比较结果,不需要配置触发,为了使比较结果更加直观,开启外部比较结果输出。9 a% Q% t1 d$ j: q8 o3 V2 w * g1 P! ^; N$ R8 B7 J 相关操作函数说明:% W( ]" o8 ]7 W- E3 |; u HAL_StatusTypeDef HAL_COMP_Start(COMP_HandleTypeDef *hcomp)7 b, p; h( G/ e 功能:开启比较器; 参数 1:比较器句柄,根据需要填写; 返回:操作结果,HAL_OK 或 HAL_ERROR;, r' F3 \" w- o$ ~1 l$ v3 @ 示例:HAL_COMP_Start(&hcomp3);// 开启 COMP3HAL_StatusTypeDef HAL_COMP_Stop(COMP_HandleTypeDef *hcomp)# v4 j7 u1 b5 o5 }% p; h) ^ 功能:关闭比较器;( g* U5 s* W7 `7 J f! A4 i 参数 1:比较器句柄,根据需要填写; 返回:操作结果,HAL_OK 或 HAL_ERROR; uint32_t HAL_COMP_GetOutputLevel(const COMP_HandleTypeDef *hcomp)* x5 r4 s9 Z/ c( P 功能:读取比较器输出电平;9 f: ]* j: e. i4 }, d, ` 参数 1:比较器句柄,根据需要填写; 返回:比较结果,COMP_OUTPUT_LEVEL_LOW 或 COMP_OUTPUT_LEVEL_HIGH; 示例:result = HAL_COMP_GetOutputLevel(&hcomp3); //软件读取比较结果2 U; R( _, V0 P2 n0 i% V 核心代码: if(HAL_COMP_Start(&hcomp3) != HAL_OK)# F2 `4 P d& [, o0 ^ //开启比较器 {" W6 \+ l1 B2 X7 v/ O) }5 u Error_Handler();1 S" A# P$ h) E, A) I/ L4 _' P& K } while (1)0 a' n- v6 G3 n3 ` { result = HAL_COMP_GetOutputLevel(&hcomp3); //软件读取比较结果 if(result == COMP_OUTPUT_LEVEL_HIGH)//比较结果为 1,即 INP 大于 INM2 \' L! ?( q$ M2 b4 y$ y {4 f/ t0 x" Y/ Q6 W1 E HAL_GPIO_TogglePin(GPIOD,GPIO_PIN_11);//翻转 LED2 }) t$ P6 J. ?+ C" p; I5 u. k. P HAL_Delay(100);+ T( K( Y9 k! y }- W/ v5 t4 C4 c3 N! A ' ?( o* K4 t2 r$ s5 u7 m) S$ Q; M 以上为 main 函数中外设初始化结束后的部分首先开启比较器,然后在主循环中每 100ms读取一次比较结果,如果结果为 1,即正相输入大于反相输入,则进行 LED2 翻转。 实验现象: 下载烧录后可以观察到拨动拨盘电位器,或者遮蔽光敏电阻时可以改变比较结果,CMP 亮时,LED2 状态不变,CMP 灭时,LED2 闪烁。 8 z8 o# H6 P* U# G2 R+ A 2、中断读取 COMP 结果实验 CubeMX 配置如下,保存后生成对应的配置代码:
7 R. ^2 e) c* Q- j( l/ v6 Z ▲ CubeMX 进行 COMP 配置: F' y- w# R5 [ ; e; k- ~ O1 |, X* k; O - J5 m; D" l# \2 b3 a( U 本实验使用中断读取 COMP 比较结果,在比较结果变化的上升沿和下降沿都触发中断,为了使比较结果更加直观,开启外部比较结果输出。 8 W8 L7 R5 g7 d4 g$ r 核心代码: if(HAL_COMP_Start(&hcomp3) != HAL_OK) //开启比较器1 r, `$ |) q- n& d$ c {* {2 Q) p. h9 G h Error_Handler();5 x4 O' c1 m s3 @, P6 J+ r } ) @7 O. @% F% ]5 J 4 f+ D* R. C2 r! f8 r( _ 以上为 main 函数中外设初始化结束后的部分,这里只需要开启比较器即可,使用函数与上例相同。 2 R1 |' d/ V$ C: ^ void HAL_COMP_TriggerCallback(COMP_HandleTypeDef* hcomp)8 l. V* I, x/ z$ ] { if(hcomp->Instance==COMP3)7 g' K% N9 D. X. Q5 [# P8 |7 M { K+ d0 M) w0 V uint8_t temp;4 D2 E _6 q. \2 X& X temp = HAL_COMP_GetOutputLevel(&hcomp3);//读取比较结果 if(temp == COMP_OUTPUT_LEVEL_HIGH)//结果为 1,上升沿触发 { HAL_GPIO_WritePin(GPIOD,GPIO_PIN_10,GPIO_PIN_RESET);//点亮 LED1$ `5 G( p" d; E1 y8 \3 u HAL_GPIO_WritePin(GPIOD,GPIO_PIN_11,GPIO_PIN_SET);//熄灭 LED2 } else0 _& E8 h3 N% O { P9 N, c6 u- R% h0 t HAL_GPIO_WritePin(GPIOD,GPIO_PIN_10,GPIO_PIN_SET);//熄灭 LED1 HAL_GPIO_WritePin(GPIOD,GPIO_PIN_11,GPIO_PIN_RESET);//点亮# [+ k1 l$ u( h1 i! Z8 g! e LED29 y [/ n( @4 U } } } 以上为 COMP 触发中断回调函数,该回调函数为比较器共用,需要判断中断源,如果开启了多个边沿,还需要判断具体边沿。实质上,COMP 中断触发与 EXTI 类似,实际就是将COMP 比较输出作为一个 IO 映射到 EXTI 进行中断。# Z7 X9 Q( z3 a! G3 ~ 1 g: E( E( [+ P" x( l2 J 实验现象:! ~8 ~3 A6 R3 l' O) @ 下载烧录后可以观察到拨动拨盘电位器,或者遮蔽光敏电阻时可以改变比较结果,CMP 亮时,LED1 灭,LED2 亮,CMP 灭时,LED1 亮,LED2 灭。! s6 j. K$ M1 G1 @, i6 E 6 ~8 ^) m8 J! R& N s5 u! `$ n 3、内部 DAC 级联比较实验 CubeMX 配置如下,保存后生成对应的配置代码:
* E* K* U N* l) A' @- U& L ▲ CubeMX 进行 COMP 配置/ H* M0 M5 e/ n' U% W5 T" ?0 w
: w2 M Z' D& y+ W# K ▲ CubeMX 进行 DAC 配置# ?" k1 O3 O) ?* C. o. x; `+ G ( B2 M. d/ I( Z0 f9 y+ x 本实验使用软件读取 COMP 比较结果,正相输入为光敏电阻,反相输入使用 DAC3 的 OUT1作为比较电压,为了使比较结果更加直观,开启外部比较结果输出。 8 g; g! h1 O t4 K B1 r 核心代码: void DAC3_CH1_Set_Vol(uint16_t vol) { double temp=vol; temp/=1000; temp=temp*4096/3.3;( Q1 [5 V( Q7 v2 u HAL_DAC_SetValue(&hdac3,DAC_CHANNEL_1,DAC_ALIGN_12B_R,temp);//12 位右对齐数据格式设置 DAC 值, Y" g# r8 d5 Z2 F8 l } " M5 e0 `2 \2 H( e9 S 以上为 DAC 设置函数,通过该函数可以将输入的电压快速转换为 DAC 所需的寄存器数据。8 Y+ s0 o. D4 k+ g8 v8 Y + R: \( C0 ]2 I DAC3_CH1_Set_Vol(500);//DAC 输出设置为 500mv HAL_DAC_Start(&hdac3,DAC_CHANNEL_1);//生效 DAC 输出# K4 R( X5 {1 m) g$ x& i if(HAL_COMP_Start(&hcomp3) != HAL_OK) //开启比较器 {- G6 x9 ]/ Q9 ^7 R V, C Error_Handler(); }) }% |! t8 u% l( c3 F6 u while (1) {$ B1 H: F0 f; x- A result = HAL_COMP_GetOutputLevel(&hcomp3); //软件读取比较结果# b) X, r9 `0 q2 I if(result == COMP_OUTPUT_LEVEL_HIGH)//比较结果为 1,即 INP 大于 INM {' W3 w6 S0 ^2 j5 H9 ~3 J HAL_GPIO_TogglePin(GPIOD,GPIO_PIN_11);//翻转 LED2! r4 j, w: T0 {2 V, v } HAL_Delay(100);) k) }( H* v7 v } 以上为 main 函数中外设初始化结束后的部分,首先设置好 DAC 输出电压,开启 DAC,然后开启比较器,最后在主循环中每 100ms 读取一次比较结果,如果结果为 1,即正相输入大于反相输入,则进行 LED2 翻转。 实验现象:# I t1 I t8 {7 }5 n9 m' z# o ? 下载烧录后可以观察到遮蔽光敏电阻时可以改变比较结果,CMP 亮时,LED2 状态不变,CMP 灭时,LED2 闪烁。 + {" H6 J* p) t) g, W / t; o# p6 o. P$ P8 ^% z- s 第十一、FLASH 实验 实验目的:掌握和熟悉 G474 内部的 FLASH 用法,包括 FLASH 读写应用等。FLASH 读取无需进行配置。 0 O/ z+ h; W- L9 [4 H8 q+ l7 C: a 核心代码: /* 函数名:FLASH_Write! U' r1 S" l m* Y; F$ Z * 描述 :flash 写入数据 * 输入 :data 写入数据地址 addr flash 地址 size 写入字节数 K2 [1 G3 O1 f- X+ R+ L9 n * 输出 :无 * 调用 :FLASH_Write(data,FLASH_PAGE_127,8);//8 字节 */ uint8_t FLASH_Write(uint8_t* data,uint32_t addr,uint16_t SIZE) { FLASH_EraseInitTypeDef EraseInitStruct;//定义擦写操作结构体 uint32_t SECTORError = 0;& q! a. J9 z8 w$ Y8 E uint64_t write_buff[10];//写入缓冲数组6 X6 w4 x0 D: I7 r0 K0 t6 G uint8_t i = 0;0 [& U2 [ n; B2 r% u! h8 T uint8_t size = SIZE/8;//8 字节写入次数 memcpy(write_buff,data,SIZE);//将输入的数组移动到写入缓冲区5 k' v- q+ T* p7 t HAL_FLASH_Unlock();//解锁; V! }7 q- v0 D3 U2 l6 t2 t2 i EraseInitStruct.Banks = FLASH_BANK_1; //存储区 1 EraseInitStruct.TypeErase = FLASH_TYPEERASE_PAGES;//页擦除6 ]0 E7 |( ~+ E" m) W EraseInitStruct.Page = (addr-0x08000000)/FLASH_PAGE_SIZE;//擦除页 EraseInitStruct.NbPages = 1;//擦除页数 if(HAL_FLASHEx_Erase(&EraseInitStruct,&SECTORError) != HAL_OK) //擦除页 { return 1;//擦除失败$ u x% Q' F% y) g2 j0 s } while(size) {7 P0 i- O1 n7 t0 f, c: U, r if(HAL_FLASH_Program(FLASH_TYPEPROGRAM_DOUBLEWORD,addr,write_buff)!= HAL_OK)//双字写入 { return 2;//写入失败; o( c3 N) |* F U1 L7 r }/ S5 x. ~& Q+ S* R( `2 Y8 ~* O. n/ U addr = addr+8;* U. H) {9 ?. Q& c; m& p& {3 A: G! \ i++; size--; }8 r$ D4 y; q6 ~6 O; j- Z* t HAL_FLASH_Lock(); //锁定 FLASH return 0;: r- V2 m# X/ E" I$ w0 } }5 G7 @7 `" f, W1 ~5 C' m: D( @ /* 函数名:FLASH_Read+ i r1 H* v; w: {2 K * 描述 :flash 读取数据" n6 f" m Q- X * 输入 :data 读取数据地址 addr flash 地址 size 读取字节数 * 输出 :无 * 调用 :FLASH_Read(data,FLASH_PAGE_127,8);//8 字节 */0 O& J+ ]3 f$ X9 S uint8_t FLASH_Read(uint8_t* data,uint32_t addr,uint16_t SIZE) {& \8 o. q* F' P* J( e+ n/ E, v uint32_t read_buff[10]; //接收缓冲数组 a w( J- h' U4 q( t uint8_t i = 0; uint16_t size = SIZE/4; //接收次数) p3 ] h% _) y9 ~4 M while(size)/ F3 x$ S, M P9 I, h { read_buff = *(__IO uint32_t*)addr; //从 FLASH 读取数据到接收缓冲数组 addr = addr+4; i++;3 D; U! h" B$ j8 y size--; } memcpy(data,read_buff,SIZE); //将数据从接收缓冲数组转移到接收数组 return 0;; c/ b0 \' _5 @- P }# W* f4 }/ a4 O/ w( e; \( K : b6 C5 ] r3 F6 S$ p7 [ & D1 L( [# Q* d" ? 主要代码:# P {& y) r- \% Q uint8_t data[8]={0,0,0,0,0,0,0,0}; 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);7 y6 [( D. i. w) b5 h 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)));! i3 G4 z6 B- T" M$ B8 z2 E snprintf(buf,30,"UID:%0x-%0x-%0x",UID1,UID2,UID3); LCD_PutString(10,60,buf,Red,White,0); 实验现象:! `/ u+ t: W, k8 Y1 P& u( K0 c1 n 下载烧录后可以观察到 LCD 显示 FLASH 测试次数以及芯片的 ID。4 k9 \ W( p; M. | `- y
▲ 实验现象 7 L- u$ [! k/ ~3 X$ t. P 第十二、单总线实验: G3 j+ P' L8 R- Y( v- W: p* ?/ i 实验目的:掌握和熟悉常用的单总线通信,包括 DS18B20,DHT11 读写应用。% D* f% \; \% x! W CubeMX 配置如下,保存后生成对应的配置代码:
▲ 图 3.12.1 CubeMX 进行 GPIO 输出配置 本实验进行单总线读取 DS18B20 和 DHT11。使用 CUBEMX 配置 IO 为输出模式。 1 F- Z" g6 Q7 T6 ~' a ' v3 \8 d! j" [0 \% f& i 读取 DS18B20 核心代码:: o3 G2 f9 A' X* s /* 函数名:DS18B20_IO_OUT * 功能:初始化 DS18B20 的 GPIO 为输出模式 * 输入:无 * 输出:无 * 备注:无: W# Z/ _$ z3 k q% Q) B! M */; @/ G5 h% I3 u% Y" k void DS18B20_IO_OUT(void ) { GPIO_InitTypeDef GPIO_InitStruct = {0};5 i' t9 k1 Y5 _5 F0 K! z* c3 b3 O GPIO_InitStruct.Pin = GPIO_PIN_9;% o, ]3 R5 t% J% E. F GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP; GPIO_InitStruct.Pull = GPIO_NOPULL; GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;9 k) [( ]/ N6 p ? HAL_GPIO_Init(GPIOD, &GPIO_InitStruct); }( T/ P+ z/ y/ ~ /* 函数名:DS18B20_IO_OUT * 功能:初始化 DS18B20 的 GPIO 为输入模式 * 输入:无 * 输出:无3 k' h& n) k; u# B. U" q * 备注:无 */void DS18B20_IO_IN(void ) {4 \+ V, E5 S6 W! W' H GPIO_InitTypeDef GPIO_InitStruct = {0};0 t0 h* H+ F( M5 _( e4 z$ i GPIO_InitStruct.Pin = GPIO_PIN_9; GPIO_InitStruct.Mode = GPIO_MODE_INPUT; GPIO_InitStruct.Pull = GPIO_NOPULL;( u2 C/ p5 ?4 G! b GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;6 ]" f6 i9 _4 J' a2 k( w HAL_GPIO_Init(GPIOD, &GPIO_InitStruct); }: z$ {4 r1 Q, J4 a /* 函数名:DS18B20_Rst * 功能:复位 DS18B20 * 输入:无 * 输出:无3 C K, z8 A/ Z0 t) x * 备注:无7 X" U. u* p# S8 n6 v */ void DS18B20_Rst(void)% M9 r! i# G% { { DS18B20_IO_OUT(); //SET PG11 OUTPUT; \9 z! m6 Y, P DQ_OUT_LOW();0 X# O0 ? t2 w2 n* \/ Z1 ], d Z2 h //拉低 DQ; r0 h/ u9 R/ o" P2 [+ ^ delay_us(750);# p; a1 V/ L" L. V% W( Y& A //拉低 750us8 X" n6 ~3 j: _2 J* l DQ_OUT_HIGH(); //DQ=1+ b5 M: h0 J8 J! w* q; P delay_us(15);+ B: [6 [% R, {0 ^( q% a //15US }8 c2 R$ m; Q% ?5 k9 N" { /* 函数名:DS18B20_Check * 功能:等待 DS18B20 的回应 * 输入:无 * 输出:返回 1:未检测到 DS18B20 的存在 返回 0:存在 * 备注:无3 x! e/ b- r$ C W- h8 k */# ^. J* _8 X9 [! R. e" z5 G5 g2 Z uint8_t DS18B20_Check(void)3 x- e8 n' R% ?( V! |2 | {. _ j9 ^" L3 r$ l. |( O uint8_t retry=0; DS18B20_IO_IN(); //SET PG11 INPUT while (DQ_GET_IN()&&retry<200) {/ H1 |' N+ r& D9 S5 g: p retry++;delay_us(1);; ?( x: e; ?( ?5 r! I6 [8 S' n };0 N0 K W O/ f! Q- \ if(retry>=200)return 1; else retry=0; while (!DQ_GET_IN()&&retry<240)! T# E! I' T% h# n- }% a {- J" B% u2 } T. K! W$ y" L retry++; delay_us(1); }; if(retry>=240)return 1;6 r& V, b1 U+ ` return 0; }4 p! P9 s* s) I5 ^* |: i /* 函数名:DS18B20_Read_Bit8 ~9 P! P* b# f5 ]9 q$ w! | * 功能:从 DS18B20 读取一个位5 |7 y. I$ e+ F. c- y * 输入:无7 Z! T! \1 [) \1 t1 N ^2 r- `7 c * 输出:返回值:1/0$ {4 ?5 }- r& ^3 O+ l/ y0 P" u( q4 T+ E * 备注:无 */ uint8_t DS18B20_Read_Bit(void)3 R9 O1 i: w! j8 \% l9 [ {& m0 u" v2 ]9 _( d uint8_t data; DS18B20_IO_OUT(); //SET PG11 OUTPUT' _2 T/ J- O7 L# p9 o/ ` DQ_OUT_LOW();delay_us(2);/ f$ B8 A* d3 w6 b" Q( T( e DQ_OUT_HIGH();1 w0 A! S* h# I8 _ DS18B20_IO_IN();7 X( W& d9 c* A" ?' I1 C$ H //SET PG11 INPUT9 D- |3 M, Q( C, n, P+ g7 N delay_us(12); if(DQ_GET_IN())data=1; else data=0; delay_us(50); return data;$ k4 @. Z1 {8 i4 a5 D }5 i9 X! E$ e- ]9 L d# V ]5 d% s /* 函数名:DS18B20_Read_Byte; ?# A- S/ y& N" u: u9 U4 d3 x, Z* H4 S * 功能:从 DS18B20 读取一个字节 * 输入:无 * 输出:返回值:读到的数据 * 备注:无 */ uint8_t DS18B20_Read_Byte(void) { uint8_t i,j,dat;7 M# c, p9 C) B dat=0;2 [7 c8 M' m* l. I/ G; ^ _ for (i=1;i<=8;i++)! d! }! z/ G% R) i+ Z+ H {+ n! |. \- G, L9 t/ A j=DS18B20_Read_Bit(); dat=(j<<7)|(dat>>1); } return dat; } /* 函数名:DS18B20_Write_Byte * 功能:写一个字节到 DS18B204 Y; u6 W9 `4 K. z+ U) q4 [ * 输入:dat:要写入的字节- E/ n6 t7 X: }2 b" | * 输出:无! Y/ X' x$ ]7 K( }" f" f * 备注:无! H- ~5 r5 O% Q: M: `5 W0 x */2 P% T4 ]; U# o( q9 U; q. m void DS18B20_Write_Byte(uint8_t dat) { uint8_t j; uint8_t testb;( G. b% \ [+ J* m0 b& n5 l DS18B20_IO_OUT(); //SET PG11 OUTPUT; for (j=1;j<=8;j++)- Z- p7 u; s% ]$ Y { testb=dat&0x01; dat=dat>>1; if (testb)! F5 d9 _; D+ H5 |* h+ R/ l {, m+ \. e7 S- Q6 d DQ_OUT_LOW(); // Write 1' x" f: h) {6 o1 g delay_us(2); DQ_OUT_HIGH(); delay_us(60); }/ v- b- z9 P( z* | else- D& F8 o' \' T! k8 w$ i+ w8 ? { DQ_OUT_LOW(); // Write 0 delay_us(60); DQ_OUT_HIGH();" |; E q) E- f+ _ delay_us(2); } } } /* 函数名:DS18B20_Start * 功能:开始温度转换 * 输入:无" F- C$ H$ c3 v% r* }0 B. P5 ? * 输出:无 \# f1 G8 S2 b& x& w9 _+ x% `7 @ * 备注:无 */" h" t2 Y; H" @/ q //3 t& Y3 C1 \. Y void DS18B20_Start(void)* h. F/ A8 s, z* \- {- @" i; p' M# e { DS18B20_Rst(); DS18B20_Check();: I; z+ w1 g9 J DS18B20_Write_Byte(0xcc);// skip rom DS18B20_Write_Byte(0x44);// convert% g( |; X/ c7 W1 H! o6 Q }' G, f8 p% I2 Z /*6 n3 b6 A, b( o" ^ 函数名:DS18B20_Start: |' `0 ~0 n5 ]+ V: v9 O, i* @ * 功能:从 ds18b20 得到温度值 精度:0.1C * 输入:无" R: v0 [( a& n# y2 I * 输出:返回值:温度值 (-55.0~125.0)7 g5 Y/ D! x# h, C5 w * 备注:无1 H! p1 L3 g* o, L1 s& a: T */" N6 c6 m% K- F short DS18B20_Get_Temp(void) { uint8_t temp;1 L" C8 _! q, F3 W9 n# W9 Z uint8_t TL,TH; short tem;DS18B20_Start (); // ds1820 start convertDS18B20_Rst();3 S# J- A/ N7 ~, X) S! z DS18B20_Check(); DS18B20_Write_Byte(0xcc);// skip rom DS18B20_Write_Byte(0xbe);// convert! x- W e0 ?" g Y7 G* j TL=DS18B20_Read_Byte(); // LSB TH=DS18B20_Read_Byte();- z$ K& q o B: I2 ]" r+ n // MSB if(TH>7)4 a. c% j4 F+ z- v3 X+ t( _ {. {9 e9 [+ Z+ g) r0 X% R TH=~TH;0 ^4 d5 Z+ [* }( R TL=~TL; temp=0;- g6 E0 `0 F$ i- J# a$ J" X: J9 s //温度为负 }else temp=1; //温度为正* ~/ m+ H4 I& j) Q) c9 n$ L tem=TH; D+ D% [7 y: P5 i- `6 i //获得高八位 tem<<=8;tem+=TL;8 p1 j, D; H' {5 {& `, u1 A7 s1 |! X //获得底八位 tem=(float)tem*0.625;( ~/ V# h# {8 I' U+ M0 G //转换+ ?9 T, ^" W' v) |6 M9 z& L char buf[20]=""; if(temp)snprintf(buf,10,"Temp:%.1f",tem/10.0); else snprintf(buf,10,"Temp:%.1f",tem/10.0);/ p& m& g% z9 Z1 o1 U3 V2 v7 r8 T$ l LCD_PutString(10, 10, buf, White,Blue,1); if(temp)return tem;9 S. N1 }7 [$ O6 A! R; [, ]+ x //返回温度值# F3 m" D9 L. e7 I' w8 Z else return -tem; } 读取 DHT11 核心代码: /* 函数名:delay_us0 K4 u8 {9 Q" ]+ g/ ^+ G% O6 A+ G * 功能:微秒延时 * 输入:delay 延时多少微秒 * 输出:无3 N! Q1 E" g, d. `7 M/ S * 备注:无 */ #define CPU_FREQUENCY_MHZ 1706 h# \' D7 A- e // STM32 时钟主频& F6 X w/ y; v9 Z9 c) x8 F* R% ?& j void delay_us(__IO uint32_t delay)7 o+ G4 a% Z9 ]+ @ {4 b: Y' x% G) {; K+ o int last, curr, val; int temp; while (delay != 0) {; A, U6 r' r/ U Q* v9 q W temp = delay > 900 ? 900 : delay; last = SysTick->VAL;( J$ h0 v, J8 T9 h: p5 h% ^ curr = last - CPU_FREQUENCY_MHZ * temp; if (curr >= 0): b" r+ O+ M) C# O7 a { do { val = SysTick->VAL;1 j' } r! E2 D0 i0 f& s }% z4 T$ x6 v! x4 l# n! s while ((val < last) && (val >= curr)); } else {6 K) ^6 b' |" }8 @, @3 c curr += CPU_FREQUENCY_MHZ * 1000;% I& t- t) L! r1 P do {! _ L) Y: V: G! n val = SysTick->VAL;" s9 I3 x4 K3 ~: S; B0 G } while ((val <= last) || (val > curr));, U \, a4 M4 C& }0 } }4 w2 U" Q- t8 a, h& o: \* b delay -= temp; } } unsigned int rec_data[4]; /* 函数名:DH11_GPIO_Init_OUT * 功能:初始化 DHT11 的 GPIO 为输出模式( F; t7 L( t, a' ~" N * 输入:无 * 输出:无6 l; E& O2 y: L9 L * 备注:无 */ void DH11_GPIO_Init_OUT(void) { GPIO_InitTypeDef GPIO_InitStruct = {0}; GPIO_InitStruct.Pin = GPIO_PIN_9; GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;" Q/ b/ y$ ]6 i9 ]% X! ]4 v- N GPIO_InitStruct.Pull = GPIO_NOPULL;5 P/ g" `8 r+ |0 n% Y. H @ GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW; HAL_GPIO_Init(GPIOD, &GPIO_InitStruct);5 Q! g0 S6 K) ^' S% v }( F- L8 O" O( A // 对于 M0 来说,是输入 /* 函数名:DH11_GPIO_Init_IN * 功能:初始化 DHT11 的 GPIO 为输入模式" H9 g# g+ P- z6 n3 i * 输入:无 * 输出:无; O! a! L8 ]1 f# r * 备注:无 */ void DH11_GPIO_Init_IN(void) { GPIO_InitTypeDef GPIO_InitStruct = {0}; GPIO_InitStruct.Pin = GPIO_PIN_9; GPIO_InitStruct.Mode = GPIO_MODE_INPUT; GPIO_InitStruct.Pull = GPIO_NOPULL;1 \; ^$ e" o, N GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;% E- ?" A: O2 o3 b) n1 M HAL_GPIO_Init(GPIOD, &GPIO_InitStruct);! J! g" L3 V: l3 I" \0 Q2 \ // DL_GPIO_initDigitalInput(GPIO_DQ_dht11_and_ds18b20_IOMUX);//配置为上拉输入- f* ^2 b$ h2 @( r* ]7 E" o } // 主机发送开始信号 /* 函数名:DHT11_Start * 功能:主机发送开始信号 y& g* J: @( Z# O( r * 输入:无. A h8 N' d( F) u% R& u * 输出:无+ w+ t/ N7 u7 p$ b5 u& ?0 P6 T * 备注:无 */ void DHT11_Start(void)3 S2 m3 c) I" p { DH11_GPIO_Init_OUT(); // 输出模式 dht11_high; // 先拉高 delay_us(30);1 _: }$ M5 _0 H, f6 m( b: s) b dht11_low; // 拉低电平至少 18ms HAL_Delay(20); dht11_high; // 拉高电平 20~40us DH11_GPIO_Init_IN(); // 输入模式0 y$ Y) j8 s" N& E( ]5 L delay_us(30); } // 获取一个字节 /* 函数名:DHT11_Rec_Byte8 ~, L6 T% J' U6 s4 Q* b * 功能:获取一个字节 * 输入:无 * 输出:读取的字符4 L- T5 i- H( r U- e b- v * 备注:无 */ char DHT11_Rec_Byte(void)7 Q) V2 h' a7 Z {- Q. c8 m# _3 u4 z- z/ `3 o- G unsigned char i = 0; unsigned char data; for (i = 0; i < 8; i++) // 1 个数据就是 1 个字节 byte,1 个字节 byte 有 8 位 bit; N2 k$ E9 L: j( \: G { while (dht11_read == 0) ; // 从 1bit 开始,低电平变高电平,等待低电平结束/ b5 j9 n1 h0 v+ _" t& W* b delay_us(30); // 延迟 30us 是为了区别数据 0 和数据 1,0 只有 26~28us data <<= 1; // 左移7 X( J' ^, w! ?/ _$ h4 f if (dht11_read == GPIO_PIN_SET) // 如果过了 30us 还是高电平的话就是数据 1 { data |= 1; // 数据+1 }8 k* ~: }2 v; H# h8 w% n$ F while (dht11_read == GPIO_PIN_SET)+ ^% Y7 v3 { O9 ]5 t% e( g ; // 高电平变低电平,等待高电平结束* V @' \4 p/ ] B$ }! U; V: _ }( D$ B$ R/ ]# T7 `) m/ k7 n8 h return data; } // 获取数据$ z2 A4 i% i6 U W5 E6 m /* 函数名:DHT11_REC_Data& B" a/ y, N- K9 d * 功能:获取 DHT11 数据并打印 * 输入:无4 n$ |6 t8 ~+ z, i- J7 U9 ?7 r u * 输出:无 * 备注:无 */% L3 u; [; Y6 @/ ^ void DHT11_REC_Data(void) {+ C7 w( f+ ?3 W: L1 ] unsigned int R_H, R_L, T_H, T_L;4 ]* q6 {7 J x6 n* q* K) ~9 ] unsigned char RH, RL, TH, TL, CHECK;. w$ d% q) N! z3 N. G% B" F DHT11_Start(); // 主机发送信号- [. ]0 T0 c- s3 ~ //dht11_high; // 拉高电平 if (dht11_read == 0) // 判断 DHT11 是否响应, [1 K, q7 j* Y! `/ D U. ^. F6 V {5 Z" s, m' P! p8 \' |/ n while (dht11_read == 0); // 低电平变高电平,等待低电平结束7 h9 E6 h; l4 U+ B! c4 }3 k3 V% H& _ while (dht11_read == GPIO_PIN_SET); // 高电平变低电平,等待高电平结束 R_H = DHT11_Rec_Byte(); R_L = DHT11_Rec_Byte(); T_H = DHT11_Rec_Byte(); T_L = DHT11_Rec_Byte();1 S9 c- M9 ^' \' X% Q CHECK = DHT11_Rec_Byte(); // 接收 5 个数据 //DH11_GPIO_Init_OUT();1 Z; L: u. e2 j5 c* G4 C //dht11_low; // 当最后一 bit 数据传送完毕后,DHT11 拉低总线 50us- O; ~9 a9 T N( ]# x9 e' p2 H6 z delay_us(55); // 这里延时 55us //dht11_high; // 随后总线由上拉电阻拉高进入空闲状态。 if (R_H + R_L + T_H + T_L == CHECK) // 和检验位对比,判断校验接收到的数据是否正确- N, p8 F7 F2 h# d/ x$ h {$ c/ G6 m v& j3 z5 K% M+ M RH = R_H;& _5 ?$ G" P4 Z3 k3 M- Q RL = R_L;* n: x3 U) [$ c4 K TH = T_H;1 b# u5 ~; l: y+ \! r TL = T_L; }# ^6 O. l# @7 S& G* h) z }+ D$ h6 A2 }) q rec_data[0] = RH; rec_data[1] = RL; rec_data[2] = TH;! {; @: ~ h+ M, X( g0 L% P rec_data[3] = TL; char buf[20]=""; snprintf(buf,10,"Temp:%d.%d",TH,TL); LCD_PutString(10, 10, buf, White,Blue,1);0 e' z0 m: f2 K* \9 s' P snprintf(buf,10,"Hum:%d.%d",RH,RL);, u& H8 m/ U+ P9 d- i LCD_PutString(10, 30, buf, White,Blue,1);} 4 F* z, r# y% p7 a9 J- e/ R 2 t0 K2 H; E9 m4 ?0 D: _ 主要代码:3 T H7 ]. y/ h" t; V, l" h while(1){4 b, p8 d7 k+ h- M i DHT11_REC_Data();//DHT11 读取 //DS18B20_Get_Temp();//18B20 读取 HAL_Delay(1000);6 F2 m( Q9 |. A+ w! T) X6 j1 x3 n }3 G( ?3 R/ C. {2 @ # U5 V5 G+ [" R, @- z& r) M" ` [ 实验现象: 下载烧录后可以观察到 LCD 显示 DS18B20 测试的温度,或者 DHT11 测量的温度和湿度。, |4 |2 T2 t5 N/ T2 j; A
" g- I, R b' k0 }7 O6 g6 v S ▲ 图 3.12.2 实验现象" @ B: P6 k+ D 第十三、独立看门狗" d( B; Z# S3 t 实验目的:掌握和熟悉独立看门狗用法,包括喂狗操作等。 ~) l' N. h( c$ O# z5 [ CubeMX 配置如下,保存后生成对应的配置代码:
▲ 图 3.13.1 CubeMX 进行独立看门狗配置& P! k7 E# |0 m$ p 本实验进行独立看门狗配置。使用 CUBEMX 配置 IO 为输出输入模式实现 LED 指示和按键读取,GPIO 配置参考上文。程序中使用按键喂狗,如果没在 1s 内喂狗,系统将会自动复位。 ) ^! ^/ a% l; j! ~ 主要代码: while (1) { if(HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_4)==0) {: |- o+ @# v" E7 K, h+ j7 \, @ HAL_Delay(5); if(HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_4)==0)0 U1 M C: ~/ H4 B4 \. [, t( } {+ `/ l' r4 _1 P7 K. X3 k7 K* ~ HAL_IWDG_Refresh(&hiwdg);. s P6 x1 t0 `7 N( | F* ? HAL_GPIO_TogglePin(GPIOD,GPIO_PIN_10); while(!HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_4)); } } /* USER CODE END WHILE */) _; |" y6 W/ V2 Z3 p3 ^ /* USER CODE BEGIN 3 */ }" k$ ?* \( s# _( o; R8 k F% `0 d ) n! C7 h- t6 d$ D4 f' ] 实验现象: 下载烧录后可以观察到如果在一定时间内进行喂狗操作,系统会复位,LCD 重新加载。& K; B3 m5 x3 F( c4 M8 t) I
▲ 图 3.13.2 实验现象 ( P8 }2 {4 [/ A$ @) X! Q/ k( ] 转载自: AI电堂 如有侵权请联系删除 $ v1 y. S/ e( V& q* A8 H/ A! Y 2 D* @7 t( x Y+ q) g" ^6 ] |
经验分享 | 基于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°相位同步方案
微信公众号
手机版