0 前言
🔥 这两年开始毕业设计和毕业答辩的要求和难度不断提升,传统的毕设题目缺少创新和亮点,往往达不到毕业答辩的要求,这两年不断有学弟学妹告诉学长自己做的项目系统达不到老师的要求。
为了大家能够顺利以及最少的精力通过毕设,学长分享优质毕业设计项目,今天要分享的是
🚩
基于PID控制的智能平衡车设计与实现
🥇学长这里给一个题目综合评分(每项满分5分)
- 难度系数:3分
- 工作量:2分
- 创新点:4分
1 简介
基于AB32的放松按摩仪,实现了40°热敷功能,气囊按摩功能,有温度显示,可按键切换
2 主要器件
- 中科蓝讯的AB32VG1单片机
- RT-Thread物联网操作系统
- NTC温度采集模块
- 碳钎维板热敷模块
- 气囊按摩模块
3 实现效果
4 硬件设计
AB32VG1主控MCU
简介
开发板采用中科蓝讯的32位RISC-V指令集的AB32VG1型号MCU,主频120M。MCU有8M的Flash,和192K SRAM。支持3.0V-5.0V供电。
与一般MCU不同的是,这款MCU具有MPU模块,就是电源管理模块,支持Charge电路、BUCK电路、LDO电路等等,手册第十页给出了MPU模块的详细参数。
开发环境搭建
根据官方的指导,使用的是RT-thread官方stduio平台,先更新软件源代码至最新版,下载中科蓝讯软件包,下载RISC-V-GCC工具链,编译程序会用到。
软件包配置
接下来选择我们本次实验用到的软件包,wavplayer软件包、optparse软件包和multibutton软件包,实现通过板载按键控制声音的播放语音量的增减。
然后对软件包进行简单配置,按键的示例代码可以勾选也可以不勾选,后面要对此进行修改,改为评测板上的用户按键,optparse软件包默认即可。
NTC温度采集:adc7 PE5
原理简介
NTC的电路如图所示
R66端的电压输出到单片机的引脚,中间有缩小系数。
具体的思路是,读取adc的电压(mV,这样更加精确),换算为电阻值,NTC在25度的时候是10K欧姆,R66是2K。那么电压就是v=2250*2/(2+NTC),计算出来就是mV的单位,图中电压是标记错误,实际是2.25V。
反推电阻R(多少千欧)=4500/V-2,然后根据这个电阻值,去计算温度值,NTC的分度表《MF52 系列 测温型 NTC 热敏电阻器》如下:
其他器件引脚
气囊按摩模块:
time3-定时器3
pwm PB0-pwm输出
PA4驱动使能脚
碳钎维板热敷模块
time5-定时器5
pwm PE1-pwm输出
PA5驱动使能脚
OLED:
SCL:PE6
SDA:PE7
按键
S2:PF1
S3:PF0
S4:PA2
5 软件说明
main线程负责初始化
int main(void)
{
// led_init();
button_init();
Pwm_Init();
temp_get_adc_init();
display_init();
rt_kprintf("Hello world!\n");
while (1)
{
rt_thread_delay(500);
}
}
display线程负责oled屏幕的显示
#include "display.h"
#include <oled.h>
static void display_entry(void *parameter)
{
OLED_Clear();
while(1)
{
OLED_ShowString(20,20, "start-up",16,1);
OLED_ShowNum(0,0,(int)((temp*100)/100),2,12,1);
OLED_ShowString(25,0, ".",16,1);
OLED_ShowNum(30,0,(int)(temp*100)%100,2,12,1);
OLED_Refresh();
rt_thread_delay(100);
}
}
rt_thread_t display=RT_NULL;
int display_init(void)
{
OLED_Init();
//创建显示线程 优先级设置为31
display = rt_thread_create("display", display_entry, RT_NULL, 1024, 28, 5);
//启动显示线程
if(RT_NULL != display)
{
rt_thread_startup(display);
// rt_kprintf("display_ok\n");
}
return 0;
}
temp_get_adc线程采集ntc的温度值并换算成摄氏度
rt_thread_t temp_get_adc=RT_NULL;
int temp_get_adc_init(void)
{
rt_err_t ret = RT_EOK;
/* 查找设备 */
adc_dev = (rt_adc_device_t)rt_device_find(ADC_DEV_NAME);
if (adc_dev == RT_NULL)
{
rt_kprintf("adc sample run failed! can't find %s device!\n", ADC_DEV_NAME);
return RT_ERROR;
}
/* 使能设备 */
ret = rt_adc_enable(adc_dev, ADC_DEV_CHANNEL);
/* 关闭通道 */
// ret = rt_adc_disable(adc_dev, ADC_DEV_CHANNEL);
//创建温度采集线程
temp_get_adc = rt_thread_create("temp_get_adc", temp_get_entry, RT_NULL, 1024, 11, 20);
//启动线程
if(RT_NULL != temp_get_adc)
{
rt_thread_startup(temp_get_adc);
}
return ret;
}
pwm_thread线程负责气泵磁阀的控制,temp_control线程负责温度的控制
rt_thread_t pwm_thread=RT_NULL,temp_control=RT_NULL;
int Pwm_Init(void)
{
pwm_dev_t5 = (struct rt_device_pwm *)rt_device_find(PWM_DEV_NAME5);
RT_ASSERT(pwm_dev_t5 != RT_NULL);
/* 设置PWM周期和脉冲宽度 */
rt_pwm_set(pwm_dev_t5, PWM_DEV_CHANNEL0, period, pulse);
/* 使能设备 */
rt_pwm_enable(pwm_dev_t5, PWM_DEV_CHANNEL0);
pwm_dev_t3 = (struct rt_device_pwm *)rt_device_find(PWM_DEV_NAME3);
RT_ASSERT(pwm_dev_t3 != RT_NULL);
rt_pwm_set(pwm_dev_t3, PWM_DEV_CHANNEL1, period, pulse);
rt_pwm_enable(pwm_dev_t3, PWM_DEV_CHANNEL1);
rt_pin_mode(DIR1, PIN_MODE_OUTPUT);
rt_pin_mode(DIR2, PIN_MODE_OUTPUT);
//创建led线程
pwm_thread = rt_thread_create("pwm_thread", pwm_entry, RT_NULL, 1024, 12, 10);
temp_control = rt_thread_create("temp_control", temp_control_entry, RT_NULL, 1024, 9, 10);
//启动led线程
if(RT_NULL != pwm_thread)
rt_thread_startup(pwm_thread);
if(RT_NULL != temp_control)
rt_thread_startup(temp_control);
return 0;
}
button_thread定时器控制按键
rt_timer_t button_thread=RT_NULL;
int button_init(void)//按键初始化
{
rt_pin_mode(S2, PIN_MODE_INPUT_PULLUP );//上拉输入
rt_pin_mode(S3, PIN_MODE_INPUT_PULLUP );//上拉输入
rt_pin_mode(S4, PIN_MODE_INPUT_PULLUP );//上拉输入
S2_sem = rt_sem_create("S2", 0, RT_IPC_FLAG_FIFO); //创建按键的信号量,当按键按下就释放信号量,在需要使用按键的地方获取信号量即可
S3_sem = rt_sem_create("S3", 0, RT_IPC_FLAG_FIFO);
S4_sem = rt_sem_create("S4", 0, RT_IPC_FLAG_FIFO);
//创建邮箱
key_mailbox = rt_mb_create("key_mailbox",5, RT_IPC_FLAG_FIFO);
key2_mailbox = rt_mb_create("key_mailbox",5, RT_IPC_FLAG_FIFO);
//创建定时器
button_thread = rt_timer_create("button", button_entry, RT_NULL, 20, RT_TIMER_FLAG_PERIODIC);
if(RT_NULL != button_thread)
{
rt_timer_start(button_thread);
// rt_kprintf("timer_button\n");
}
return 0;
}
key_mailbox,key2_mailbox邮箱负责线程间的通信 按键和temp_control、pwm_thread的通信
int button_init(void)//按键初始化
{
rt_pin_mode(S2, PIN_MODE_INPUT_PULLUP );//上拉输入
rt_pin_mode(S3, PIN_MODE_INPUT_PULLUP );//上拉输入
rt_pin_mode(S4, PIN_MODE_INPUT_PULLUP );//上拉输入
S2_sem = rt_sem_create("S2", 0, RT_IPC_FLAG_FIFO); //创建按键的信号量,当按键按下就释放信号量,在需要使用按键的地方获取信号量即可
S3_sem = rt_sem_create("S3", 0, RT_IPC_FLAG_FIFO);
S4_sem = rt_sem_create("S4", 0, RT_IPC_FLAG_FIFO);
//创建邮箱
key_mailbox = rt_mb_create("key_mailbox",5, RT_IPC_FLAG_FIFO);
key2_mailbox = rt_mb_create("key_mailbox",5, RT_IPC_FLAG_FIFO);
//创建定时器
button_thread = rt_timer_create("button", button_entry, RT_NULL, 20, RT_TIMER_FLAG_PERIODIC);
if(RT_NULL != button_thread)
{
rt_timer_start(button_thread);
// rt_kprintf("timer_button\n");
}
return 0;
}
5 最后