在参加完15届蓝桥杯嵌入式后,意犹未尽,又做了个小项目……
1.模块简介
该装置有几个部分:
1.平台搭建
7.主函数
8.PCB制板
接下来一块一块分开来讲
2.平台搭建
由于是基于HAL库开发,那就打开最熟悉的STM32CubeMX
引脚配置
基础部分
oled显示模块(OUTPUT)
PB6 ----------SCL
PB7----------SDA
俩按键(INPUT)
PB0----------K1
PB1----------K2
几个LED(OUTPUT)
PA5----------LD1
PA6----------LD2
控制风扇的(OUTPUT、PWM)
PA8----------ENB
PB14--------IN3
PB15--------IN4
利用TIM1产生PWM波,配置按照下图
控制加热板的(利用继电器)
PA7----------加热板+
ESP8266部分(UART)
PA3 UART2_RX
PA2 UART2_TX
其他脚按照下图接线
由于用到串口,故把串口的配置附上(利用异步时钟,波特率选择115200Bit/s,大家记得点中断!!)
无线透传部分
由于大家用的模块都不大一样,可以参考这个网站进行配置,真的大开眼界无线DAP-LINK使用指南 | 允斯工作室 (yunsi.studio)
时钟树配置
3.dht11读取温湿度
直接上代码,这一部分就是调用dht11的代码,然后在循环里调用测温度和湿度的函数即可。
dht11.c部分
#include "dht11.h"
#include "tim.h"
void DHT11_IO_IN(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.Pin = GPIO_PIN_0;
GPIO_InitStructure.Mode = GPIO_MODE_INPUT;
HAL_GPIO_Init(GPIOA,&GPIO_InitStructure);
}
void DHT11_IO_OUT(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.Pin = GPIO_PIN_0;
GPIO_InitStructure.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStructure.Speed = GPIO_SPEED_FREQ_HIGH;
HAL_GPIO_Init(GPIOA,&GPIO_InitStructure);
}
//复位DHT11
void DHT11_Rst(void)
{
DHT11_IO_OUT(); //设置为输出
DHT11_DQ_OUT_LOW; //拉低DQ
HAL_Delay(20); //拉低至少18ms
DHT11_DQ_OUT_HIGH; //DQ=1
delay_us(30); //主机拉高20~40us
}
//等待DHT11的回应
//返回1:未检测到DHT11的存在
//返回0:存在
uint8_t DHT11_Check(void)
{
uint8_t retry=0;
DHT11_IO_IN(); //设置为输出
while (DHT11_DQ_IN&&retry<100)//DHT11会拉低40~80us
{
retry++;
delay_us(1);
};
if(retry>=100)return 1;
else retry=0;
while (!DHT11_DQ_IN&&retry<100)//DHT11拉低后会再次拉高40~80us
{
retry++;
delay_us(1);
};
if(retry>=100)return 1;
return 0;
}
//从DHT11读取一个位
//返回值:1/0
uint8_t DHT11_Read_Bit(void)
{
uint8_t retry=0;
while(DHT11_DQ_IN&&retry<100)//等待变为低电平
{
retry++;
delay_us(1);
}
retry=0;
while(!DHT11_DQ_IN&&retry<100)//等待变高电平
{
retry++;
delay_us(1);
}
delay_us(40);//等待40us
if(DHT11_DQ_IN)return 1;
else return 0;
}
//从DHT11读取一个字节
//返回值:读到的数据
uint8_t DHT11_Read_Byte(void)
{
uint8_t i,dat;
dat=0;
for (i=0;i<8;i++)
{
dat<<=1;
dat|=DHT11_Read_Bit();
}
return dat;
}
//从DHT11读取一次数据
//temp:温度值(范围:0~50°)
//humi:湿度值(范围:20%~90%)
//返回值:0,正常;1,读取失败
uint8_t DHT11_Read_Data(uint16_t *temp,uint16_t *humi)
{
uint8_t buf[5];
uint8_t i;
DHT11_Rst();
if(DHT11_Check()==0)
{
for(i=0;i<5;i++)//读取40位数据
{
buf[i]=DHT11_Read_Byte();
}
if((buf[0]+buf[1]+buf[2]+buf[3])==buf[4])
{
*humi=(buf[0]<<8) + buf[1];
*temp=(buf[2]<<8) + buf[3];
}
}else return 1;
return 0;
}
//初始化DHT11的IO口 DQ 同时检测DHT11的存在
//返回1:不存在
//返回0:存在
uint8_t DHT11_Init(void)
{
DHT11_Rst();
return DHT11_Check();
}
dht11.h部分
#ifndef __DHT11_H__
#define __DHT11_H__
#include "main.h"
#define DHT11_DQ_OUT_HIGH HAL_GPIO_WritePin(GPIOA,GPIO_PIN_0,GPIO_PIN_SET)
#define DHT11_DQ_OUT_LOW HAL_GPIO_WritePin(GPIOA,GPIO_PIN_0,GPIO_PIN_RESET)
#define DHT11_DQ_IN HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_0)
//IO方向设置
void DS18B20_IO_IN(void);
void DS18B20_IO_OUT(void);
uint8_t DHT11_Init(void);//初始化DHT11
uint8_t DHT11_Read_Data(uint16_t *temp,uint16_t *humi);//读取温湿度
uint8_t DHT11_Read_Byte(void);//读出一个字节
uint8_t DHT11_Read_Bit(void);//读出一个位
uint8_t DHT11_Check(void);//检测是否存在DHT11
void DHT11_Rst(void);//复位DHT11
#endif
4.esp8266上传阿里云
先看效果,可以在阿里云上同步观测到dht11测量到的温度和湿度
手机端也可以看到
接下来上代码,同样是.c和.h文件,主要是配置三大件有点麻烦,但不要紧只要阿里云那边的三大件都对的上,连接起来还是非常快的
.c部分
#include "esp.h"
#include "usart.h"
#include "string.h"
#include "stdio.h"
#include "dht11.h"
extern unsigned char receive_data ;
extern uint16_t Temperature;
extern uint16_t Shidu;
extern uint8_t Lock;
extern uint8_t Switch2;
extern uint8_t string[200];
extern char RECS[250];
extern uint8_t led_status;
//const char* WIFI ="iPhone";
//const char* WIFIASSWORD="1122334455";
const char* WIFI ="202";
const char* WIFIASSWORD="ZLSYS959";
const char* ClintID="k11lrEXZkfv.temp|securemode=2\\,signmethod=hmacsha256\\,timestamp=1712202199197|";
const char* username="temp&k11lrEXZkfv";
const char* passwd="e033ec75050e41da5c29b92faa2201d6b422d63f2c464131eebaf7b9c7c21629";
const char* Url="k11lrEXZkfv.iot-as-mqtt.cn-shanghai.aliyuncs.com";
const char* pubtopic="/sys/k11lrEXZkfv/temp/thing/service/property/set";
const char* subtopic="/sys/k11lrEXZkfv/temp/thing/event/property/post";
#define SUB_TOPIC "/sys/k11lrEXZkfv/temp/thing/service/property/set"
#define PUB_TOPIC "/sys/k11lrEXZkfv/temp/thing/event/property/post"
//#define JSON_FORMAT "{\\\"params\\\":{\\\"temp\\\":%d\\,\\\"humi\\\":%d\\}\\,\\\"version\\\":\\\"1.0.0\\\"}"
const char* func1="temp";
const char* func2="humi";
const char* func3="powerstate_2";
const char* func4="LEDSwitch";
//#define SUB_TOPIC "/sys/k0zjr3AtVcM/mqtt_stm32/thing/service/property/set"
//#define PUB_TOPIC "/sys/k0zjr3AtVcM/mqtt_stm32/thing/event/property/post"
#define JSON_FORMAT "{\\\"params\\\":{\\\"temp\\\":%d\\,\\\"humi\\\":%d\\}\\,\\\"version\\\":\\\"1.0.0\\\"}"
char RECS[250];
unsigned char i;
char esp_Init(void)
{
memset(RECS,0,sizeof(RECS));
sprintf((char *)string,"AT+RST\r\n"); //重启
HAL_UART_Transmit(&huart2, string, strlen((char *)string), 50);
HAL_Delay(2000);
memset(RECS,0,sizeof(RECS));
sprintf((char *)string,"ATE0\r\n"); //关闭回显
HAL_UART_Transmit(&huart2, string, strlen((char *)string), 50);
HAL_Delay(10);
if(strcmp(RECS,"OK"))
return 1;
sprintf((char *)string,"AT+CWMODE=1\r\n"); //Station模式
HAL_UART_Transmit(&huart2, string, strlen((char *)string), 50);
HAL_Delay(1000);
if(strcmp(RECS,"OK"))
return 2;
memset(RECS,0,sizeof(RECS));
sprintf((char *)string,"AT+CWJAP=\"%s\",\"%s\"\r\n",WIFI,WIFIASSWORD); //连接热点 //Station模式
HAL_UART_Transmit(&huart2, string, strlen((char *)string), 50);
HAL_Delay(2000);
if(strcmp(RECS,"OK"))
return 3;
memset(RECS,0,sizeof(RECS));
sprintf((char *)string,"AT+MQTTUSERCFG=0,1,\"%s\",\"%s\",\"%s\",0,0,\"\"\r\n",ClintID,username,passwd);//用户信息配置
HAL_UART_Transmit(&huart2, string, strlen((char *)string), 50);
HAL_Delay(10);
if(strcmp(RECS,"OK"))
return 4;
memset(RECS,0,sizeof(RECS));
sprintf((char *)string,"AT+MQTTCONN=0,\"%s\",1883,1\r\n",Url); //连接服务器
HAL_UART_Transmit(&huart2, string, strlen((char *)string), 50);
HAL_Delay(1000);
if(strcmp(RECS,"OK"))
return 5;
sprintf((char *)string,"AT+MQTTSUB=0,\"%s\",1\r\n",subtopic); //订阅消息
HAL_UART_Transmit(&huart2, string, strlen((char *)string), 50);
HAL_Delay(500);
if(strcmp(RECS,"OK"))
return 6;
memset(RECS,0,sizeof(RECS));
return 0;
}
//功能:esp发送消息
//参数:无
//返回值:0:发送成功;1:发送失败
char Esp_PUB(void)
{
memset(RECS,0,sizeof(RECS));
//printf("AT+MQTTPUB=0,\"%s\",\"{\\\"method\\\":\\\"thing.event.property.post\\\"\\,\\\"params\\\":{\\\"%s\\\":%d\\,\\\"%s\\\":%d\\,\\\"%s\\\":%d\\,\\\"%s\\\":%d}}\",0,0\r\n",pubtopic,func1,Temperature,func2,Shidu,func3,Lock,func4,Switch2);
// sprintf((char *)string,"AT+MQTTPUB=0,\""PUB_TOPIC"\",\""JSON_FORMAT"\",0,0\r\n",Temperature,Shidu);
sprintf((char *)string,"AT+MQTTPUB=0,\""PUB_TOPIC"\",\""JSON_FORMAT"\",0,0\r\n",Temperature>>8,Shidu>>8);
HAL_UART_Transmit(&huart2, string, strlen((char *)string), 50);
// HAL_UART_Transmit(&huart1, string, strlen((char *)string), 50);
// while(RECS[0]);//等待ESP返回数据
HAL_Delay(200);//延时等待数据接收完成
if(strcmp(RECS,"ERROR")==0)
return 1;
return 0;
}
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
if(huart->Instance == USART2)
{
RECS[i++] = receive_data;
if((RECS[i-2] == '\r') || (RECS[i-1] == '\n'))
{
RECS[i-2] = '\0';
i = 0;
CommandAnalyse();
}
// led_status^=0x01;
HAL_UART_Receive_IT(&huart2, &receive_data, 1);//串口2接收1位数据
}
}
void CommandAnalyse(void)
{
if(strncmp(RECS,"+MQTTSUBRECV:",13)==0)
{
uint8_t i=0;
while(RECS[i++] != '\0')
{
if(strncmp((RECS+i),func3,9)==0) //func3,3 数值3是func3字符串长度
{
while(RECS[i++] != ':');
Lock=RECS[i]-'0';
}
if(strncmp((RECS+i),func4,9)==0)
{
while(RECS[i++] != ':');
led_status=RECS[i]-'0';
}
}
}
}
.h部分
#include "main.h"
char esp_Init(void);
char Esp_PUB(void);
void CommandAnalyse(void);
阿里云查看三大件的方法:
5.L298N控制舵机pid算法
电机控制部分
简单来说,电机的控制就是控制它正转、反转、转的速度
正转和反转只需要让配置的引脚发出01或者10就可以正转和反转了(我用的是PB14和PB15),想让它转的快那就把PA8输出的占空比大些转的就快了,重要的PWM波生成的配置(STM32CubeMX里面的配置)
PA8----------ENB
PB14--------IN3
PB15--------IN4
那么先来看.c部分
#include "motor.h"
void Motor_Set(int Motor)
{
TIM1->ARR=19999;
//1.先根据正负设置方向GPIO 高低电平
if(Motor <0)
{A_SET;B_RESET;}
else
{A_RESET;B_SET;}
//2.然后设置占空比
if(Motor <0)
{
if(Motor <-99) Motor =-99;
TIM1->CCR1=20000*(-Motor)/100;
// __HAL_TIM_SET_COMPARE(&htim1, TIM_CHANNEL_1, (100+Motor1));
}
else
{
if(Motor >99) Motor = 99;
TIM1->CCR1=20000*Motor/100;
// __HAL_TIM_SET_COMPARE(&htim1, TIM_CHANNEL_1,Motor1);
}
}
.h部分
#include "main.h"
#define A_SET HAL_GPIO_WritePin(GPIOB,GPIO_PIN_15,GPIO_PIN_SET)
#define B_SET HAL_GPIO_WritePin(GPIOB,GPIO_PIN_14,GPIO_PIN_SET)
#define A_RESET HAL_GPIO_WritePin(GPIOB,GPIO_PIN_15,GPIO_PIN_RESET)
#define B_RESET HAL_GPIO_WritePin(GPIOB,GPIO_PIN_14,GPIO_PIN_RESET)
void Motor_Set(int Motor);
PID介入
好啦重头戏,pid算法,先上代码,其实网上的代码很多,但难的就是如何用在自己的装置上
.c部分
#include "pid.h"
//定义一个结构体类型变量
tPid pidMotor1Speed;
tPid pidT;
//给结构体类型变量赋初值
void PID_init()
{
pidMotor1Speed.actual_val=0.0;
pidMotor1Speed.target_val=3.00;
pidMotor1Speed.err=0.0;
pidMotor1Speed.err_last=0.0;
pidMotor1Speed.err_sum=0.0;
pidMotor1Speed.Kp=10;
pidMotor1Speed.Ki=5;
pidMotor1Speed.Kd=0;
pidT.actual_val=0.0;
pidT.target_val=20;
pidT.err=0.0;
pidT.err_last=0.0;
pidT.err_sum=0.0;
pidT.Kp=-30;
pidT.Ki=0;
pidT.Kd=0;
}
//比例p调节控制函数
float P_realize(tPid * pid,float actual_val)
{
pid->actual_val = actual_val;//传递真实值
pid->err = pid->target_val - pid->actual_val;//当前误差=目标值-真实值
//比例控制调节 输出=Kp*当前误差
pid->actual_val = pid->Kp*pid->err;
return pid->actual_val;
}
//比例P 积分I 控制函数
float PI_realize(tPid * pid,float actual_val)
{
pid->actual_val = actual_val;//传递真实值
pid->err = pid->target_val - pid->actual_val;//当前误差=目标值-真实值
pid->err_sum += pid->err;//误差累计值 = 当前误差累计和
//使用PI控制 输出=Kp*当前误差+Ki*误差累计值
pid->actual_val = pid->Kp*pid->err + pid->Ki*pid->err_sum;
return pid->actual_val;
}
// PID控制函数
float PID_realize(tPid * pid,float actual_val)
{
pid->actual_val = actual_val;//传递真实值
pid->err = pid->target_val - pid->actual_val;当前误差=目标值-真实值
pid->err_sum += pid->err;//误差累计值 = 当前误差累计和
//使用PID控制 输出 = Kp*当前误差 + Ki*误差累计值 + Kd*(当前误差-上次误差)
pid->actual_val = pid->Kp*pid->err + pid->Ki*pid->err_sum + pid->Kd*(pid->err - pid->err_last);
//保存上次误差: 这次误差赋值给上次误差
pid->err_last = pid->err;
return pid->actual_val;
}
.h部分
#ifndef __PID_H
#define __PID_H
//声明一个结构体类型
typedef struct
{
float target_val;//目标值
float actual_val;//实际值
float err;//当前偏差
float err_last;//上次偏差
float err_sum;//误差累计值
float Kp,Ki,Kd;//比例,积分,微分系数
} tPid;
//声明函数
float P_realize(tPid * pid,float actual_val);
void PID_init(void);
float PI_realize(tPid * pid,float actual_val);
float PID_realize(tPid * pid,float actual_val);
#endif
我来说明一下我是如何用pid使得温度恒温的。
首先我们会给一个目标值,比方说我们设定目标温度为20°C,但dht11测到的温度是28°C,那么实际与我们的设定就有8°C的偏差,那么这个8°C就会被PID算法算出来,然后我们直接把它丢到motor电机驱动的代码里,电机就会疯狂吹风(基于你给的p值(比例参数)),让它降温,直到降到目标值,是个负反馈的过程 。
最终效果就是,温度高了,风扇呼呼转,温度低了,加热板工作
6.esp32无限透传功能
无线DAP-LINK使用指南 | 允斯工作室 (yunsi.studio)
经过这个设置过后,直接就可以在keil里进行无线下载,无线是最好的电气隔离,再也不怕烧电脑主板了哈哈哈……
7.简单的三行代码按键和led显示
别问,问就是学电子设计工坊学的
.c部分
#include "main.h"
#define KB1 HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_0)
#define KB2 HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_1)
#define KEYPORT KB1|(KB2<<1)|0x00
unsigned char trg;
unsigned char cont;
unsigned char key_lock;
void key_read(void)
{
unsigned char readdata=(KEYPORT)^0xff;
if(key_lock==0)
trg=cont&(cont^readdata);
cont=readdata;
}
.h部分
#include "main.h"
extern unsigned char trg;
extern unsigned char cont;
extern unsigned char key_lock;
void key_read(void);
8.主函数
#include "main.h"
#include "tim.h"
#include "usart.h"
#include "gpio.h"
#include "led_key.h"
#include "oled.h"
#include "string.h"
#include "stdio.h"
#include "dht11.h"
#include "uart_printf.h"
#include "esp.h"
#include "core_json.h"
#include "motor.h"
#include "pid.h"
void SystemClock_Config(void);
unsigned char receive_data = 0;
uint8_t str[21];
uint8_t string[200];
uint8_t led_status;
uint16_t Temperature;
uint16_t Shidu;
uint8_t Lock;
uint8_t Switch2;
extern tPid pidT;
//uart
unsigned char rx_buf[10],uart_buf[2];
uint16_t rx_cnt;
unsigned char rx1_buf[40];
void dht_process()
{
DHT11_Read_Data(&Temperature,&Shidu);
}
//key
__IO uint32_t keyTick;
uint16_t key_cnt;
unsigned char desk=0;
unsigned char ld=0;
void key_process()
{
if((uwTick - keyTick)<100) return;//减速函数
keyTick = uwTick;
if(cont==0) key_lock=0;
key_read();
if(trg&0x01&&trg&0x02)
{
if(++desk==2)
desk=0;
}
if(trg&0x01&&desk==1)
{
pidT.target_val+=0.5;
}
if(trg&0x02&&desk==1)
{
pidT.target_val-=0.5;
}
}
//led
void led_process()
{
if((int)pidT.target_val<(Temperature>>8))
ld=1;
else
ld=0;
if(ld==1)
{
HAL_GPIO_WritePin(GPIOA,GPIO_PIN_5,GPIO_PIN_SET); //加热灯
HAL_GPIO_WritePin(GPIOA,GPIO_PIN_6,GPIO_PIN_RESET); //冷却灯
HAL_GPIO_WritePin(GPIOA,GPIO_PIN_7,GPIO_PIN_RESET); //加热装置
// HAL_GPIO_WritePin(GPIOC,GPIO_PIN_13,GPIO_PIN_RESET);
}
else
{
HAL_GPIO_WritePin(GPIOA,GPIO_PIN_5,GPIO_PIN_RESET);
HAL_GPIO_WritePin(GPIOA,GPIO_PIN_6,GPIO_PIN_SET);
HAL_GPIO_WritePin(GPIOA,GPIO_PIN_7,GPIO_PIN_SET); //加热装置
// HAL_GPIO_WritePin(GPIOC,GPIO_PIN_13,GPIO_PIN_SET);
}
}
//MOTOR
void mot_process()
{
// Motor_Set(0);
Motor_Set(PID_realize(&pidT,Temperature>>8));
}
//oled
__IO uint32_t oledTick;
void oled_process()
{
if((uwTick - oledTick)<1000) return;//减速函数
oledTick = uwTick;
unsigned char buf[40];
if(desk==0)
{
sprintf((char *)buf," <DHT11> ");
OLED_ShowString(0,0,buf,16);//这个是oled驱动里面的,是显示位置的一个函数,
sprintf((char *)buf," T:%d.%d C ",Temperature>>8,Temperature&0xff);
OLED_ShowString(0,3,buf,16);//这个是oled驱动里面的,是显示位置的一个函数,
sprintf((char *)buf," H:%d.%d %% ",Shidu>>8,Shidu&0xff);
OLED_ShowString(0,5,buf,16);//这个是oled驱动里面的,是显示位置的一个函数,
}
if(desk==1)
{
sprintf((char *)buf," <MOTOR+T> ");
OLED_ShowString(0,0,buf,16);//这个是oled驱动里面的,是显示位置的一个函数,
sprintf((char *)buf," NOW:%d.%d C ",Temperature>>8,Temperature&0xff);
OLED_ShowString(0,3,buf,16);//这个是oled驱动里面的,是显示位置的一个函数,
sprintf((char *)buf," SET:%.1f C ",pidT.target_val);
OLED_ShowString(0,5,buf,16);//这个是oled驱动里面的,是显示位置的一个函数,
}
if(Esp_PUB() == 1)
{
OLED_ShowString(1,1,"publish failed",16);
HAL_Delay(500);
OLED_Clear();
}
}
int main(void)
{
HAL_Init();
SystemClock_Config();
MX_GPIO_Init();
MX_USART2_UART_Init();
MX_TIM3_Init();
MX_TIM1_Init();
OLED_Init(); //初始化OLED
OLED_Clear();
uint8_t Judge=0;
OLED_ShowString(1,3," Linking...",16);
HAL_UART_Receive_IT(&huart2,uart_buf, 1);
do
{
Judge = esp_Init();
sprintf((char *)str,"error code:%d",Judge);
OLED_ShowString(0,2,str,16);
}
while(Judge); //连接阿里云直到成功
sprintf((char *)str, "Ali-Cloud OK!");
OLED_ShowString(0,0,str,16);
HAL_Delay(500);
OLED_Clear();
HAL_TIM_PWM_Start_IT(&htim1,TIM_CHANNEL_1);
PID_init();
while (1)
{
oled_process();
dht_process();
key_process();
led_process();
mot_process();
}
}
void SystemClock_Config(void)
{
RCC_OscInitTypeDef RCC_OscInitStruct = {0};
RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
/** Initializes the RCC Oscillators according to the specified parameters
* in the RCC_OscInitTypeDef structure.
*/
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
RCC_OscInitStruct.HSEState = RCC_HSE_ON;
RCC_OscInitStruct.HSEPredivValue = RCC_HSE_PREDIV_DIV1;
RCC_OscInitStruct.HSIState = RCC_HSI_ON;
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL9;
if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
{
Error_Handler();
}
/** Initializes the CPU, AHB and APB buses clocks
*/
RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
|RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2;
RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;
if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2) != HAL_OK)
{
Error_Handler();
}
}
void Error_Handler(void)
{
__disable_irq();
while (1)
{
}
}
#ifdef USE_FULL_ASSERT
void assert_failed(uint8_t *file, uint32_t line)
{
}
#endif
9.PCB制板
白嫖嘉立创嘿嘿……
直接上文件
控制电机的引脚直接从底部出来,好走线
10.一个简单的3D制图
一个门外汉随便画着玩的,为了给dht11一个家,想到它的功能可以测温度和湿度,就想到百叶箱,就给它用soildwork建了个模型
效果如下: