当前位置: 首页 > news >正文

【毕业设计】 基于单片机的放松按摩仪设计与实现 - 物联网 嵌入式 stm32

文章目录

  • 0 前言
  • 1 简介
  • 2 主要器件
  • 3 实现效果
    • 4 硬件设计
      • **AB32VG1主控MCU**
      • NTC温度采集:adc7 PE5
      • 其他器件引脚
  • 5 软件说明
  • 5 最后


0 前言

🔥 这两年开始毕业设计和毕业答辩的要求和难度不断提升,传统的毕设题目缺少创新和亮点,往往达不到毕业答辩的要求,这两年不断有学弟学妹告诉学长自己做的项目系统达不到老师的要求。

为了大家能够顺利以及最少的精力通过毕设,学长分享优质毕业设计项目,今天要分享的是

🚩 基于PID控制的智能平衡车设计与实现

🥇学长这里给一个题目综合评分(每项满分5分)

  • 难度系数:3分
  • 工作量:2分
  • 创新点:4分

🧿 选题指导, 项目分享:

https://gitee.com/dancheng-senior/project-sharing-1/blob/master/%E6%AF%95%E8%AE%BE%E6%8C%87%E5%AF%BC/README.md


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 最后

相关文章:

  • 基于springboot,vue校园点餐系统
  • qmake 文件扩展名操作
  • 【javaweb简单教程】9.Servlet与过滤器超简单教程
  • 猛淦,阿里大老纯手撸Spring高级源码手册,啃起来真TM香啊
  • ES6导入与导出(module)
  • 【opencv-c++】cv::imshow和cv::waitKey函数显示图像
  • 第二课 我的第一个程序 hello world
  • 消息队列实现分布式事务
  • 前端性能优化指标 + 检测工具
  • CubeMx笔记 --pwm输出+输入捕获
  • 轻松玩转树莓派Pico之一、新手上路
  • 目前我国网络安全人才市场状况
  • Redis源码解读之用RedisAe实现一个简单的HTTP服务器
  • 【极简python】第一章 print与变量
  • HAL库与Cubemx\rt-thread Nano系列教程-01-新建HAL工程及移植RT-Nano到Alios Developer Kit
  • 【刷算法】从上往下打印二叉树
  • 2018以太坊智能合约编程语言solidity的最佳IDEs
  • Django 博客开发教程 16 - 统计文章阅读量
  • Eureka 2.0 开源流产,真的对你影响很大吗?
  • Fabric架构演变之路
  • JAVA多线程机制解析-volatilesynchronized
  • js递归,无限分级树形折叠菜单
  • leetcode98. Validate Binary Search Tree
  • Less 日常用法
  • Making An Indicator With Pure CSS
  • weex踩坑之旅第一弹 ~ 搭建具有入口文件的weex脚手架
  • 人脸识别最新开发经验demo
  • 容器化应用: 在阿里云搭建多节点 Openshift 集群
  • 适配iPhoneX、iPhoneXs、iPhoneXs Max、iPhoneXr 屏幕尺寸及安全区域
  • 在Docker Swarm上部署Apache Storm:第1部分
  • ionic异常记录
  • 国内唯一,阿里云入选全球区块链云服务报告,领先AWS、Google ...
  • 支付宝花15年解决的这个问题,顶得上做出十个支付宝 ...
  • 组复制官方翻译九、Group Replication Technical Details
  • ​一文看懂数据清洗:缺失值、异常值和重复值的处理
  • #{} 和 ${}区别
  • (1)(1.9) MSP (version 4.2)
  • (31)对象的克隆
  • (C语言)球球大作战
  • (ISPRS,2023)深度语义-视觉对齐用于zero-shot遥感图像场景分类
  • (SpringBoot)第七章:SpringBoot日志文件
  • (vue)el-checkbox 实现展示区分 label 和 value(展示值与选中获取值需不同)
  • (论文阅读11/100)Fast R-CNN
  • (免费领源码)python#django#mysql校园校园宿舍管理系统84831-计算机毕业设计项目选题推荐
  • (转)一些感悟
  • (转载)CentOS查看系统信息|CentOS查看命令
  • ***汇编语言 实验16 编写包含多个功能子程序的中断例程
  • *2 echo、printf、mkdir命令的应用
  • .NET 2.0中新增的一些TryGet,TryParse等方法
  • .NET Core 控制台程序读 appsettings.json 、注依赖、配日志、设 IOptions
  • .Net MVC4 上传大文件,并保存表单
  • .net on S60 ---- Net60 1.1发布 支持VS2008以及新的特性
  • .net2005怎么读string形的xml,不是xml文件。
  • .net的socket示例
  • [ NOI 2001 ] 食物链