第十、COMP 实验, b- l, N/ I: t) a 实验目的:掌握和熟悉 G474 内部的模拟比较器用法,包括触发方式以及与内部 DAC 级联使用等。' y7 Q/ _( ]6 e6 a0 M' Z 1、软件读取 COMP 结果实验 CubeMX 配置如下,保存后生成对应的配置代码: ▲ CubeMX 进行 COMP 配置' |3 F% `- C2 n4 t! f 本实验使用软件读取 COMP 比较结果,不需要配置触发,为了使比较结果更加直观,开启外部比较结果输出。9 }! R- I# C% S' C4 Z; p# {. A q: Q3 v' S5 @& Q, j 2 y) Z' b$ { J5 _, n 相关操作函数说明:- }4 ^5 Z2 ^( H1 z# Z HAL_StatusTypeDef HAL_COMP_Start(COMP_HandleTypeDef *hcomp)0 K& }1 f6 `* C9 X 功能:开启比较器; 参数 1:比较器句柄,根据需要填写; 返回:操作结果,HAL_OK 或 HAL_ERROR; l: Z& \1 i# w( B: S% b 示例:HAL_COMP_Start(&hcomp3);// 开启 COMP3HAL_StatusTypeDef HAL_COMP_Stop(COMP_HandleTypeDef *hcomp) 功能:关闭比较器; 参数 1:比较器句柄,根据需要填写; 返回:操作结果,HAL_OK 或 HAL_ERROR;. `7 R! q- R+ Y/ g8 s' U uint32_t HAL_COMP_GetOutputLevel(const COMP_HandleTypeDef *hcomp) 功能:读取比较器输出电平; 参数 1:比较器句柄,根据需要填写;! P% J7 I/ b( P2 D/ L" N 返回:比较结果,COMP_OUTPUT_LEVEL_LOW 或 COMP_OUTPUT_LEVEL_HIGH; 示例:result = HAL_COMP_GetOutputLevel(&hcomp3); //软件读取比较结果8 q. H3 O& B; i 8 s1 F9 a& ?" y7 l9 B+ A$ K 核心代码:# s8 ~9 _% q; L3 v1 P8 ~, d! } if(HAL_COMP_Start(&hcomp3) != HAL_OK) //开启比较器 {; `/ _# M z6 W( [) U) h0 [( P8 i Error_Handler(); }1 A }) s; i4 V, m9 J while (1) {6 W6 s# D; t$ t result = HAL_COMP_GetOutputLevel(&hcomp3); //软件读取比较结果. j: P( w0 `- f if(result == COMP_OUTPUT_LEVEL_HIGH)//比较结果为 1,即 INP 大于 INM& A9 U7 ?8 n! B5 C5 R3 L3 C& I {; U$ p0 g# ~; _8 r) \) _ HAL_GPIO_TogglePin(GPIOD,GPIO_PIN_11);//翻转 LED2 }6 V/ b1 X* X7 o1 F' Z HAL_Delay(100);8 g% h. u& O# }9 h, x. Z6 v3 Q6 S } 7 y/ N5 b# t( a; \1 T" i2 E 以上为 main 函数中外设初始化结束后的部分首先开启比较器,然后在主循环中每 100ms读取一次比较结果,如果结果为 1,即正相输入大于反相输入,则进行 LED2 翻转。 实验现象: 下载烧录后可以观察到拨动拨盘电位器,或者遮蔽光敏电阻时可以改变比较结果,CMP 亮时,LED2 状态不变,CMP 灭时,LED2 闪烁。 2 I% e( U+ \7 f. M- f) [ 2、中断读取 COMP 结果实验 CubeMX 配置如下,保存后生成对应的配置代码: ( [5 [% U6 {, L$ v1 \ ▲ CubeMX 进行 COMP 配置1 s7 Q2 P7 r: G( N+ B& K" b0 G 本实验使用中断读取 COMP 比较结果,在比较结果变化的上升沿和下降沿都触发中断,为了使比较结果更加直观,开启外部比较结果输出。 $ v x9 N% u* e ?7 v2 x# A 核心代码:8 m" a3 E: h/ M) Z if(HAL_COMP_Start(&hcomp3) != HAL_OK) //开启比较器 { Error_Handler(); }6 g! l+ b- k( [% U 0 G3 t5 p$ [) e 以上为 main 函数中外设初始化结束后的部分,这里只需要开启比较器即可,使用函数与上例相同。$ |" s. A( j: f9 `- \6 a* P7 L void HAL_COMP_TriggerCallback(COMP_HandleTypeDef* hcomp) {' n- {( l9 Y: d$ G1 `0 p if(hcomp->Instance==COMP3)% \2 q; k' P: P% H { uint8_t temp; temp = HAL_COMP_GetOutputLevel(&hcomp3);//读取比较结果 if(temp == COMP_OUTPUT_LEVEL_HIGH)//结果为 1,上升沿触发 { HAL_GPIO_WritePin(GPIOD,GPIO_PIN_10,GPIO_PIN_RESET);//点亮* C" Q1 \4 X. {- P8 x( F LED1 HAL_GPIO_WritePin(GPIOD,GPIO_PIN_11,GPIO_PIN_SET);//熄灭 LED29 t! H$ d) ]% U7 i }2 ]7 \2 _% k7 P1 [7 ]5 \0 m else& C& }- Z! k: N, c1 F$ e { HAL_GPIO_WritePin(GPIOD,GPIO_PIN_10,GPIO_PIN_SET);//熄灭 LED1 HAL_GPIO_WritePin(GPIOD,GPIO_PIN_11,GPIO_PIN_RESET);//点亮6 G( A8 c0 [: a, { LED2! z8 n6 v+ i& t* p% n3 n+ S }, t2 `' j* T# X4 L" \* W1 \ }0 J, U8 o/ W+ @1 c }% b; \; B6 F( h4 q ; h" x8 i$ I6 o3 {: ]- L 以上为 COMP 触发中断回调函数,该回调函数为比较器共用,需要判断中断源,如果开启了多个边沿,还需要判断具体边沿。实质上,COMP 中断触发与 EXTI 类似,实际就是将COMP 比较输出作为一个 IO 映射到 EXTI 进行中断。 0 ^2 [. G) z) g E1 ~! U! o I0 x 5 i, L, P8 j( Q3 Q 实验现象:8 w0 d) E3 ], z. m 下载烧录后可以观察到拨动拨盘电位器,或者遮蔽光敏电阻时可以改变比较结果,CMP 亮时,LED1 灭,LED2 亮,CMP 灭时,LED1 亮,LED2 灭。 3、内部 DAC 级联比较实验 CubeMX 配置如下,保存后生成对应的配置代码:4 h( M6 f7 F1 K) _: P. V, c ) I* l4 H7 g& |' [ ▲ CubeMX 进行 COMP 配置 ▲ CubeMX 进行 DAC 配置! }+ X; O3 f: `6 O- g; K 本实验使用软件读取 COMP 比较结果,正相输入为光敏电阻,反相输入使用 DAC3 的 OUT1作为比较电压,为了使比较结果更加直观,开启外部比较结果输出。8 X& [* }' s) {7 F" T2 c3 y 4 ^( M! l3 }+ ]( Q Q 9 ^& |$ a! H$ W5 W& B7 o. s+ d 核心代码: void DAC3_CH1_Set_Vol(uint16_t vol)! `* y3 a6 g* d9 d: S8 I! Q {4 Y; @5 ~8 i7 O7 T3 z; i# s double temp=vol; temp/=1000;) Y9 O0 l* k! k& y" M temp=temp*4096/3.3; HAL_DAC_SetValue(&hdac3,DAC_CHANNEL_1,DAC_ALIGN_12B_R,temp);//12 位右对齐数据格式设置 DAC 值 }$ A; Q5 L0 ]# L( \7 U |& H4 Y& a* H1 P % e8 g# g8 |! P& k3 z3 q 以上为 DAC 设置函数,通过该函数可以将输入的电压快速转换为 DAC 所需的寄存器数据。2 V' P! o& `/ C9 U* ^3 P$ ~ $ \$ L" [1 A$ @+ l1 {: s0 Z Z DAC3_CH1_Set_Vol(500);//DAC 输出设置为 500mv# ]. H! T9 {) h# C! V% S HAL_DAC_Start(&hdac3,DAC_CHANNEL_1);//生效 DAC 输出" U) q) F5 [6 K: M1 i; q5 N. n, M if(HAL_COMP_Start(&hcomp3) != HAL_OK) //开启比较器( z+ @. F' ^; k% W" u4 E6 {& c {/ f) H- z9 `6 q9 ~$ a* f. H4 A Error_Handler();/ m$ x& N9 y& T) W4 y8 I) c% V( t } while (1)/ S3 I8 l/ q6 Z { result = HAL_COMP_GetOutputLevel(&hcomp3); //软件读取比较结果3 X* i0 a g- J/ h( f if(result == COMP_OUTPUT_LEVEL_HIGH)//比较结果为 1,即 INP 大于 INM, w7 M+ D" U5 w7 d9 O% C {0 ?; X. ]; i% C$ v HAL_GPIO_TogglePin(GPIOD,GPIO_PIN_11);//翻转 LED2 } HAL_Delay(100); } 以上为 main 函数中外设初始化结束后的部分,首先设置好 DAC 输出电压,开启 DAC,然后开启比较器,最后在主循环中每 100ms 读取一次比较结果,如果结果为 1,即正相输入大于反相输入,则进行 LED2 翻转。: t, d, b( B' H) K 6 U3 z/ m* I, l4 S1 o: h: W 实验现象:7 v: H4 U( i5 _# g& m2 x6 D# H1 Y- I 下载烧录后可以观察到遮蔽光敏电阻时可以改变比较结果,CMP 亮时,LED2 状态不变,CMP 灭时,LED2 闪烁。. |) p/ s7 l. q 1 {( Z) z# s, i1 T, R& Y* V / C9 ~/ ?! a4 y, B( M7 k( d 第十一、FLASH 实验; M+ s! u) H# f 实验目的:掌握和熟悉 G474 内部的 FLASH 用法,包括 FLASH 读写应用等。FLASH 读取无需进行配置。 A" ?& a( }3 f) J+ a0 m3 |4 D / ]4 l) p8 A& R# V " {; w& n/ `; L" D. V8 E0 m8 | x7 w 核心代码:6 t& T5 U% u# H8 n9 d& p/ l /* 函数名:FLASH_Write * 描述 :flash 写入数据+ ?; D# w J( V( I$ ` * 输入 :data 写入数据地址 addr flash 地址 size 写入字节数 * 输出 :无 * 调用 :FLASH_Write(data,FLASH_PAGE_127,8);//8 字节 */ uint8_t FLASH_Write(uint8_t* data,uint32_t addr,uint16_t SIZE)) [7 p- C+ V3 w { FLASH_EraseInitTypeDef EraseInitStruct;//定义擦写操作结构体# F1 B+ Q5 \) v) o6 \, w uint32_t SECTORError = 0;9 o2 t3 Y( _( t6 H# ]9 n uint64_t write_buff[10];//写入缓冲数组 uint8_t i = 0;( I# ]6 h* v* k2 t1 @ uint8_t size = SIZE/8;//8 字节写入次数6 _, h# G7 h) O" t" M memcpy(write_buff,data,SIZE);//将输入的数组移动到写入缓冲区 HAL_FLASH_Unlock();//解锁 EraseInitStruct.Banks = FLASH_BANK_1; //存储区 1 EraseInitStruct.TypeErase = FLASH_TYPEERASE_PAGES;//页擦除 EraseInitStruct.Page = (addr-0x08000000)/FLASH_PAGE_SIZE;//擦除页 EraseInitStruct.NbPages = 1;//擦除页数 if(HAL_FLASHEx_Erase(&EraseInitStruct,&SECTORError) != HAL_OK) //擦除页. O" T% q) [( J) Y {7 r% V+ y" @4 \4 f. c/ Q/ L return 1;//擦除失败 }! `: H' J3 k1 S' { while(size) {# s" t: K1 l4 d, a2 ^4 ~8 D if(HAL_FLASH_Program(FLASH_TYPEPROGRAM_DOUBLEWORD,addr,write_buff)!= HAL_OK)//双字写入- ]1 t# }0 t/ q+ g: c { return 2;//写入失败7 w. U+ x; ]$ w, I) ^ } addr = addr+8; i++;4 l* r$ w! j5 Y# U* }) u size--; }8 ~$ o6 v& P' w. s* B; A1 y HAL_FLASH_Lock(); //锁定 FLASH return 0; } /* 函数名:FLASH_Read, U0 ~, p( B6 a" i! y * 描述 :flash 读取数据* q. L( P; @ x+ ^1 u6 C * 输入 :data 读取数据地址 addr flash 地址 size 读取字节数 * 输出 :无+ v# o8 h& N3 Q! k6 H( l * 调用 :FLASH_Read(data,FLASH_PAGE_127,8);//8 字节 */ uint8_t FLASH_Read(uint8_t* data,uint32_t addr,uint16_t SIZE)* X/ s% `+ N( e; g {7 F1 p6 b+ t# L uint32_t read_buff[10]; //接收缓冲数组" y. L5 l9 @5 U( @3 Z, h& _0 w uint8_t i = 0; uint16_t size = SIZE/4; //接收次数8 r' Y* o# U% t& t( Y1 N while(size)* x6 D" r* w' t) @ {/ g" b' w" ]* t ^4 D. F0 {# ?2 ^ read_buff = *(__IO uint32_t*)addr; //从 FLASH 读取数据到接收缓冲数组7 X" A" Q, E n, @, b, P addr = addr+4; i++; size--; }; w; b8 d& o- B2 }7 I9 F- Z) z memcpy(data,read_buff,SIZE); //将数据从接收缓冲数组转移到接收数组( {! i, }& C$ ]. E: R8 l return 0;. J; t) a0 g: l, c( D0 ^5 P! r/ W } ! |8 N* ]4 N3 {$ f {( [ g 主要代码: 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); 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);3 q- k/ X$ T5 x' h/ P! W$ X( Q LCD_PutString(10,60,buf,Red,White,0); 实验现象: 下载烧录后可以观察到 LCD 显示 FLASH 测试次数以及芯片的 ID。 ▲ 实验现象1 Z" W+ x* q ^3 i- r% T1 f7 U" l 第十二、单总线实验 实验目的:掌握和熟悉常用的单总线通信,包括 DS18B20,DHT11 读写应用。9 F' L* I- v5 t CubeMX 配置如下,保存后生成对应的配置代码:% o$ ?, r# T1 a% { ▲ 图 3.12.1 CubeMX 进行 GPIO 输出配置9 Y7 ~' z, Z0 F9 j. H / p+ D* d( |9 b2 G5 v) o! d 本实验进行单总线读取 DS18B20 和 DHT11。使用 CUBEMX 配置 IO 为输出模式。 ! G3 r: a- H, q& g" I7 ? 读取 DS18B20 核心代码: /* 函数名:DS18B20_IO_OUT * 功能:初始化 DS18B20 的 GPIO 为输出模式' t: g/ Y, @- m6 k1 h/ c7 V * 输入:无 * 输出:无 * 备注:无 */ void DS18B20_IO_OUT(void )$ `% {& D$ ^# V& t: t { 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; HAL_GPIO_Init(GPIOD, &GPIO_InitStruct);6 {' f) {& v, G1 O! O6 e }/ x _) t% k" w- |2 x6 }* d /* 函数名:DS18B20_IO_OUT * 功能:初始化 DS18B20 的 GPIO 为输入模式, d+ n( g# B2 T: N& K" W) _ * 输入:无- A: a$ Y$ l' o l p6 l+ R# a6 t% W * 输出:无$ G. }. o+ N/ z+ Q: V- P s * 备注:无$ x2 x' f2 n; J3 N9 Z8 s# u! I */void DS18B20_IO_IN(void ) { GPIO_InitTypeDef GPIO_InitStruct = {0};8 Q9 u9 |2 W, W- S+ u. b+ q' A; s GPIO_InitStruct.Pin = GPIO_PIN_9; GPIO_InitStruct.Mode = GPIO_MODE_INPUT;2 X# g* s% ^' G& x- j GPIO_InitStruct.Pull = GPIO_NOPULL; GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW; HAL_GPIO_Init(GPIOD, &GPIO_InitStruct);* S3 ]1 v' E; y: k; q9 \+ C* [ } /* 函数名:DS18B20_Rst * 功能:复位 DS18B20) v1 P c, g9 }/ [ * 输入:无 * 输出:无 * 备注:无 *// u% f1 j1 t/ {% @ void DS18B20_Rst(void) {" I/ J5 {9 s1 f DS18B20_IO_OUT(); //SET PG11 OUTPUT6 L) V! V+ h8 u4 l0 G1 F! @& G DQ_OUT_LOW(); //拉低 DQ delay_us(750);( H& y, [( g6 W- Z2 x, B5 d //拉低 750us DQ_OUT_HIGH();! ~5 L, f, t. O4 ` //DQ=1 delay_us(15);( t7 _) |$ C5 ?, n1 e0 Z //15US3 x8 C% U# H' C% H }7 y, N! F" f ?, d: [ /* 函数名:DS18B20_Check * 功能:等待 DS18B20 的回应/ L! t3 ~0 s' [. ~; j * 输入:无 * 输出:返回 1:未检测到 DS18B20 的存在 返回 0:存在 * 备注:无, }$ l4 ?5 j; n5 U2 { */ uint8_t DS18B20_Check(void): G: Y: f# Z0 ]( b9 c! I {4 s" G4 K6 R" Y) M6 X: b uint8_t retry=0;4 U# `5 P1 j( H3 H7 ]+ o" C( a DS18B20_IO_IN(); //SET PG11 INPUT% @/ ^! Q2 W& D( N while (DQ_GET_IN()&&retry<200)) a2 @9 }# H5 |$ ]+ _4 m3 V6 L {+ U$ l7 S8 k- z- k3 |7 \ retry++;delay_us(1);7 ^- z2 ?5 ~& H4 s5 \* _ }; if(retry>=200)return 1; else retry=0;) o* ^7 h8 k$ s9 W/ o' ~6 P while (!DQ_GET_IN()&&retry<240) {) w8 z, g1 E+ ~( H. ?& ~* @0 v retry++; delay_us(1); };; a( T6 ^' |- F: G$ u. S0 b if(retry>=240)return 1; return 0; } /* 函数名:DS18B20_Read_Bit * 功能:从 DS18B20 读取一个位 * 输入:无 * 输出:返回值:1/0 * 备注:无 */' d3 [' R- A4 a uint8_t DS18B20_Read_Bit(void) { uint8_t data;% }: q- H5 T8 X; v* l8 ?0 }0 K DS18B20_IO_OUT(); //SET PG11 OUTPUT2 j7 _7 C$ U) U% _) W DQ_OUT_LOW();delay_us(2);' N& T! C0 l( X' V DQ_OUT_HIGH();2 C( ^" K" q2 ]1 K! u4 U DS18B20_IO_IN(); //SET PG11 INPUT delay_us(12);7 K* {3 i0 y6 z6 ?6 e if(DQ_GET_IN())data=1;2 g; R4 \2 X2 ]( j else data=0;+ P* d( F% T4 ~8 Q* t, K8 u delay_us(50); return data; } /* 函数名:DS18B20_Read_Byte% X% }2 ]/ R4 R * 功能:从 DS18B20 读取一个字节1 L3 i5 u0 R6 } * 输入:无- l5 d' [: Z+ d8 o$ |. \ * 输出:返回值:读到的数据/ E% o3 I6 ~' f; ~) H * 备注:无. E2 [/ v8 L1 q5 e4 J# Y M9 ~% x$ Y */ uint8_t DS18B20_Read_Byte(void)9 f8 R6 |' X7 [0 I7 s {- e7 A7 `) m0 m2 X- V0 x uint8_t i,j,dat; dat=0;, d) @9 n& N9 d for (i=1;i<=8;i++) { j=DS18B20_Read_Bit();7 F; g& h) G; l) h; E4 r- T% m" O dat=(j<<7)|(dat>>1); } return dat; }" o) J4 o" n& E+ M; a+ x /* 函数名:DS18B20_Write_Byte. H3 {3 b6 ]6 q( H * 功能:写一个字节到 DS18B20 * 输入:dat:要写入的字节 * 输出:无5 W! T: _- V4 d6 n2 q8 a * 备注:无 */ void DS18B20_Write_Byte(uint8_t dat) { uint8_t j; uint8_t testb;9 U3 U: h! S# t2 ?3 x0 t DS18B20_IO_OUT(); //SET PG11 OUTPUT; for (j=1;j<=8;j++)" v; S: d* G+ c$ c { testb=dat&0x01; dat=dat>>1;7 @6 F" i. S) D5 D3 G; T if (testb)1 y& }$ Y4 Q% N7 X0 Z {# \$ ^9 R$ t4 E+ H$ M DQ_OUT_LOW(); // Write 1# ?" r* g$ [4 A2 q& h5 a8 I delay_us(2);. C. }4 }" h% h; T& } DQ_OUT_HIGH();6 t. ^2 ?' M3 e2 N) B, L9 ?8 d1 h8 R/ B delay_us(60); }% T' n- V& ]* c8 w" @ else: ^* f; g; E3 j Z; L2 A& C% T) w( E; k { DQ_OUT_LOW(); // Write 0: S5 W! p& ~1 o( X" j delay_us(60);# S( U7 p/ r* R; D, X DQ_OUT_HIGH(); delay_us(2); } }& p5 O0 ?( e v! w2 y } /* 函数名:DS18B20_Start * 功能:开始温度转换 * 输入:无 * 输出:无 I) S I* ^" K8 p$ l( _3 c * 备注:无. r1 d8 ~0 c! R# C */' O: @ W, L/ F' b3 a0 i //4 ^0 ]3 i8 Z$ q9 u( o' O3 @ void DS18B20_Start(void) {: o" ?6 x: n, R- B% G DS18B20_Rst();& J, K* M3 H/ z4 `; L+ X+ { DS18B20_Check(); DS18B20_Write_Byte(0xcc);// skip rom: X, v* ]/ h0 W! m# A! r. L W DS18B20_Write_Byte(0x44);// convert Y4 k! [ I: K4 G( Z# k2 F } /* 函数名:DS18B20_Start * 功能:从 ds18b20 得到温度值 精度:0.1C$ s& `$ b" f: |8 ?1 }/ z/ U: H/ r * 输入:无 * 输出:返回值:温度值 (-55.0~125.0) * 备注:无 */ short DS18B20_Get_Temp(void) {* ~- j8 x' ?, h5 f" h+ D uint8_t temp;" _$ F) Q, I$ Y2 f+ z uint8_t TL,TH; short tem;DS18B20_Start (); // ds1820 start convertDS18B20_Rst();0 Q" W% k- d: |8 s" C; P DS18B20_Check();0 h5 ?3 t; }2 x$ a% z DS18B20_Write_Byte(0xcc);// skip rom9 G$ Y- Z5 o# t! U0 G DS18B20_Write_Byte(0xbe);// convert TL=DS18B20_Read_Byte();5 g& ~, ~4 A- L' T' m // LSB$ q* D0 V3 N d5 m" _ TH=DS18B20_Read_Byte();0 [- Q1 c' k8 X6 k6 j // MSB if(TH>7) {8 x1 ~9 x/ u) Q TH=~TH; TL=~TL;% B9 t/ |/ v* R) Z- | temp=0;! u! s# g# I& D+ `0 S //温度为负 }else temp=1; //温度为正 tem=TH;* c+ C7 f+ F: l( R% o! A0 w //获得高八位9 m. R f$ A/ ^4 X6 V7 j9 `# K tem<<=8;tem+=TL; //获得底八位 tem=(float)tem*0.625;: N+ _5 q9 w; [& z //转换) H1 J' I; X% h$ Q" g char buf[20]="";. d5 @4 b+ d: F if(temp)snprintf(buf,10,"Temp:%.1f",tem/10.0); else snprintf(buf,10,"Temp:%.1f",tem/10.0);; `* [( F' H( j8 S) Y& D LCD_PutString(10, 10, buf, White,Blue,1); if(temp)return tem;2 P0 m# @$ p7 s, l6 y5 S7 u0 y1 l //返回温度值 else return -tem; }6 r6 [) [4 A% X9 f) G) } 读取 DHT11 核心代码: /* 函数名:delay_us) s. Y. {3 \% u) c2 { u0 [ * 功能:微秒延时' n. B2 ?* ~) K d2 O! p D * 输入:delay 延时多少微秒 * 输出:无; K& o$ V3 s* `% N, z * 备注:无: u# N9 B8 G2 ?7 {6 j8 H* @ */ #define CPU_FREQUENCY_MHZ 170 // STM32 时钟主频 void delay_us(__IO uint32_t delay)9 @: R+ L5 y9 J { int last, curr, val;: z4 v' F5 T4 b; N int temp; while (delay != 0)2 {1 [2 {. K3 P { temp = delay > 900 ? 900 : delay;' B( o3 f8 C$ L% C) o1 X4 G last = SysTick->VAL;! Y( O# ?3 e/ r' @& e) L/ f curr = last - CPU_FREQUENCY_MHZ * temp; if (curr >= 0) { do, Z. N# u& x+ a0 v { val = SysTick->VAL; }7 g& F' w8 [2 \, G while ((val < last) && (val >= curr));/ D9 I$ L% N. t g1 V3 k M } else { curr += CPU_FREQUENCY_MHZ * 1000;- ?; \! u$ @3 h- i2 J% d: B do {, ]1 |2 ]$ r/ u8 D# U1 X0 e val = SysTick->VAL;" J2 p: R' p; B G } while ((val <= last) || (val > curr));2 w: s1 A" G9 u% F h, M, d7 \ } delay -= temp;. a2 H( I6 X. B( w# O; B; Y } }9 R' J, r' B/ y/ U" `! J# k1 | unsigned int rec_data[4]; /* 函数名:DH11_GPIO_Init_OUT5 x, I& A- Y4 i" v * 功能:初始化 DHT11 的 GPIO 为输出模式* u6 ^( b1 {7 P$ E+ s) ? * 输入:无& @3 v! C+ G1 f+ | * 输出:无8 s/ S1 L) h9 P. B+ G& l$ R * 备注:无 */; V( F$ ^% s$ {) n) } void DH11_GPIO_Init_OUT(void). I0 L1 l, n2 Q( \1 {# G4 a: b {- g2 b! Q) F; s& B0 | GPIO_InitTypeDef GPIO_InitStruct = {0};; J i- f; O" H7 B2 l& q. o GPIO_InitStruct.Pin = GPIO_PIN_9; GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;: F! y8 e* `$ |% P9 c+ J GPIO_InitStruct.Pull = GPIO_NOPULL;5 A! o* M( p B- K GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW; HAL_GPIO_Init(GPIOD, &GPIO_InitStruct); }$ N, I" u5 a# q8 g: J 3 x" s! J, X: v: E Z7 E' |7 A% }( ` // 对于 M0 来说,是输入 /* 函数名:DH11_GPIO_Init_IN * 功能:初始化 DHT11 的 GPIO 为输入模式/ i( ^) u* w% }9 y * 输入:无 * 输出:无3 f! y" d$ }- V$ e& C * 备注:无 */ void DH11_GPIO_Init_IN(void) {+ ]1 F$ E6 F. {# Z4 w GPIO_InitTypeDef GPIO_InitStruct = {0};8 B* U. r$ q" w7 q- K5 t9 T3 y: W GPIO_InitStruct.Pin = GPIO_PIN_9;& S6 Q9 N3 E3 K1 l; Y# [ GPIO_InitStruct.Mode = GPIO_MODE_INPUT; GPIO_InitStruct.Pull = GPIO_NOPULL; GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;6 ^* a& p0 u. ?4 P8 |4 i HAL_GPIO_Init(GPIOD, &GPIO_InitStruct);5 X# [( c$ T- t // DL_GPIO_initDigitalInput(GPIO_DQ_dht11_and_ds18b20_IOMUX);//配置为上拉输入$ e9 F0 H9 |: G' Z$ v } // 主机发送开始信号 /* 函数名:DHT11_Start/ i2 C1 B; K& p3 K * 功能:主机发送开始信号# K1 d7 U% C' t& I * 输入:无; s, k7 E! c! z" y o- W1 h. y q * 输出:无 * 备注:无 */ void DHT11_Start(void)8 C" y" |7 u# \1 |( | {3 e+ p; [- j" I3 t DH11_GPIO_Init_OUT(); // 输出模式 dht11_high; // 先拉高' ~4 Y7 {& B3 n/ M delay_us(30);/ `* j! u& j; {) O1 o* I. f dht11_low; // 拉低电平至少 18ms HAL_Delay(20); dht11_high; // 拉高电平 20~40us DH11_GPIO_Init_IN(); // 输入模式 delay_us(30); } // 获取一个字节; t9 c1 T+ I- N: J" w3 I /* 函数名:DHT11_Rec_Byte6 y9 B4 C m8 p7 M' Y * 功能:获取一个字节7 z+ W) Z0 n8 b; {1 Y * 输入:无 * 输出:读取的字符 * 备注:无 */ char DHT11_Rec_Byte(void)9 k/ m/ D, S$ L' y& } { unsigned char i = 0; unsigned char data; for (i = 0; i < 8; i++) // 1 个数据就是 1 个字节 byte,1 个字节 byte 有 8 位 bit {# m& @* E0 ]0 Y1 o4 q while (dht11_read == 0) ; // 从 1bit 开始,低电平变高电平,等待低电平结束% s3 F5 k0 O3 P5 r* g1 s delay_us(30); // 延迟 30us 是为了区别数据 0 和数据 1,0 只有 26~28us p' Y( E$ G0 d" X) R data <<= 1; // 左移7 o, T: M& ~( x! m8 Q if (dht11_read == GPIO_PIN_SET) // 如果过了 30us 还是高电平的话就是数据 12 \/ B; m$ u( m' G/ M { data |= 1; // 数据+1 } while (dht11_read == GPIO_PIN_SET) ; // 高电平变低电平,等待高电平结束 } return data; }0 G. b5 n2 e9 `) K" q! X2 X' C- P // 获取数据1 T! A& Z3 t: [3 Y; F1 x+ ` /* 函数名:DHT11_REC_Data: u0 Q8 n1 |, w' a * 功能:获取 DHT11 数据并打印: I `1 K% [" v3 f9 @1 R * 输入:无 * 输出:无2 X5 O6 I: r0 G& i6 O0 j- N% o * 备注:无, u8 _, W. a9 l# j. [! y: X */ void DHT11_REC_Data(void), h) ?. }$ d2 W) d- a' f, _ {& G( T4 g2 R" J- e- Q unsigned int R_H, R_L, T_H, T_L; unsigned char RH, RL, TH, TL, CHECK; DHT11_Start(); // 主机发送信号5 @% B; _& G; \6 ^% x" s8 n //dht11_high; // 拉高电平4 l) w5 ]6 [+ [% ~6 ? if (dht11_read == 0) // 判断 DHT11 是否响应: ^6 \6 x% W7 d9 e6 K {* h4 ~$ `2 Z- M7 P) ^ E) E while (dht11_read == 0); // 低电平变高电平,等待低电平结束. v5 ?3 Z7 o7 w+ n9 b ^ while (dht11_read == GPIO_PIN_SET); // 高电平变低电平,等待高电平结束 R_H = DHT11_Rec_Byte();7 k9 N* ]% L, t0 X R_L = DHT11_Rec_Byte(); T_H = DHT11_Rec_Byte(); T_L = DHT11_Rec_Byte();5 ^5 S$ x, |0 j- z9 v. S# E# @( {2 b CHECK = DHT11_Rec_Byte(); // 接收 5 个数据 //DH11_GPIO_Init_OUT(); //dht11_low; // 当最后一 bit 数据传送完毕后,DHT11 拉低总线 50us delay_us(55); // 这里延时 55us //dht11_high; // 随后总线由上拉电阻拉高进入空闲状态。 if (R_H + R_L + T_H + T_L == CHECK) // 和检验位对比,判断校验接收到的数据是否正确 { RH = R_H;- R& [7 s! k" S8 y6 M RL = R_L;9 a8 O6 K+ |( G0 _$ f. _ TH = T_H;2 A. @' P8 z, [5 H. N TL = T_L;- O7 x6 b- h) Z- x- Y9 m }1 ]1 h T+ L. {- b! } } ( o ?8 b, x$ |! t( c9 J/ r rec_data[0] = RH; rec_data[1] = RL;/ E$ ^# r+ s, c+ r2 H1 j- H rec_data[2] = TH; rec_data[3] = TL; char buf[20]="";1 v% Q7 |1 h5 p8 _+ U& B snprintf(buf,10,"Temp:%d.%d",TH,TL);+ S8 l& N J( c- ?. w LCD_PutString(10, 10, buf, White,Blue,1);, L& x$ y# p# y4 s2 N snprintf(buf,10,"Hum:%d.%d",RH,RL); w2 p& O1 q- c- N( M$ k LCD_PutString(10, 30, buf, White,Blue,1);}, O5 [4 G! P0 @3 z 主要代码:! y2 ? K+ [$ P7 H0 H$ L) ?+ M while(1){ DHT11_REC_Data();//DHT11 读取 //DS18B20_Get_Temp();//18B20 读取 HAL_Delay(1000); }, b, r- t& N: j+ {- e 实验现象: 下载烧录后可以观察到 LCD 显示 DS18B20 测试的温度,或者 DHT11 测量的温度和湿度。- r) s, h% O# S n# G* x# E) }# L ▲ 图 3.12.2 实验现象0 o. f. v0 F8 \, _8 D0 ` 第十三、独立看门狗6 ?& d' K E. E' x8 J: v, A% z4 m1 h( y/ o 实验目的:掌握和熟悉独立看门狗用法,包括喂狗操作等。 d- u4 z2 l. S7 n) {' W CubeMX 配置如下,保存后生成对应的配置代码: ▲ 图 3.13.1 CubeMX 进行独立看门狗配置7 V: E/ n F+ a ^ - h- L- D; z, f+ W+ V 本实验进行独立看门狗配置。使用 CUBEMX 配置 IO 为输出输入模式实现 LED 指示和按键读取,GPIO 配置参考上文。程序中使用按键喂狗,如果没在 1s 内喂狗,系统将会自动复位。 主要代码:( H7 P0 h/ a' v z( j) A; d while (1) {) d0 S8 d( u. i# ` if(HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_4)==0)5 M7 D6 @- i$ b# _ { HAL_Delay(5);" z+ W Q8 I, W) s; f5 x. u if(HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_4)==0)( U$ L& z- S) W {( V- H' j8 E$ ~0 E7 ^$ i HAL_IWDG_Refresh(&hiwdg);, i% J& G& O% C W5 [ HAL_GPIO_TogglePin(GPIOD,GPIO_PIN_10); while(!HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_4)); }( P% f4 x$ k, {) K- ~ }" d; Q @; g# x4 J /* USER CODE END WHILE */ /* USER CODE BEGIN 3 */ } ) R+ K1 g. `' q, m% A' ]7 I 6 l+ a; Z3 k* Q( C 实验现象: 下载烧录后可以观察到如果在一定时间内进行喂狗操作,系统会复位,LCD 重新加载。, S$ s- A/ J$ C ▲ 图 3.13.2 实验现象/ j2 L& u8 ?( w0 V5 D: G z3 F 转载自: AI电堂 如有侵权请联系删除4 M8 k0 L5 l% z% ?* ^% W( g % u+ R) {/ z. @& X2 c- W. s ) K' P5 ~4 K0 o( C4 H ' Z1 U u9 Q& k* K/ P) p. m+ j- C |
【学习指南】基于STM32G474VET6 开发板实验经验分享(二)
【学习指南】基于STM32G474VET6 开发板基础实验经验分享一
【学习指南】基于STM32G474软件平台安装与使用教程
【学习指南】基于STM32G474VET6 开发板硬件资源解析
STM32 Explore | 基于STM32G474的STM32Cube生态系统线下培训
STM32固件库分享,超全系列整理
STM32G47x 双 Bank 模式下在线升级
基于STM32G473ZET6开发板设计经验分享
详细讲解STM32G4的软件工具和环境搭建
【经验分享】STM32G4_CORDIC与定点带符号整数数据格式