基于stm32f4的蓝牙控制小车
1. 引言
蓝牙的创始人是瑞典爱立信公司,蓝牙技术是一种无限数据与语音通信的开放性全球规范,它以低成本的近距离无线连接为基础,为固定与移动设备通信环境建立一个特别连接。手机之间通过蓝牙实现数据共享成为常理,将手机变为遥控器为人们的生活带来无限方便。遥控小车在工业、国防、科研等领域应用越来越广泛,例如说:消防遥控小车、探测小车等。本文详细阐述了使用蓝牙通信的手机遥控小车前行、倒退、左转、右转和停止等功能的软硬件设计过程。
2. 系统方案
该系统分为电机驱动模块、电源管理模块、主控板、蓝牙通信模块、android控制端等5个模块

3、系统硬件设计
3.1 电机驱动模块
3.1.1 L298N的封装
H 桥电路虽然有着诸多的优点,但是在实际制作过程中,由于元件较多,电路的搭建也较为麻烦,增加了硬件设计的复杂度。
由于H 桥电路有诸多的优点,但是在实际制作过程中电路又比较麻烦,因此在本设计中我们采用H 桥集成电机驱动芯片L298。L298N 的工作原理和以上介绍的H 桥相同,引脚图如图:

3.1.2 L298N的原理图设计
L298N 是ST 公司生产的一种高电压、大电流电机驱动芯片。该芯片采用15 脚封装。主要特点是:工作电压高,最高工作电压可达46V;输出电流大,瞬间 峰值电流可达3A,持续工作电流为2A;额定功率25W。内含两个H 桥的高电压大电流全桥式驱动器,可以用来驱动直流电动机和步进电动机、继电器线圈等感性负载;采用标准逻辑电平信号控制;具有两个使能控制端,在不受输入信号影响的情况下允许或禁止器件工作有一个逻辑电源输入端,使内部逻辑电路部分在低电 压下工作;可以外接检测电阻,将变化量反馈给控制电路。使用L298N 芯片驱动电机,该芯片可以驱动一台两相步进电机或四相步进电机,也可以驱动两台直流电机。L298 的参考电路图如图

3.1.3 电机驱动模块实物
1、控制板内部带5V逻辑电平转换芯片,不需要额外的5V供电。
2、如果EN1、EN2、EN3、EN4不接PWM调速信号,次脚需要和对应的管教短接。
3、GND和12V接电机电源,其中12V可以接7V--24V电平,板子上的电源开关只是控制逻辑5V电平的开关,L298的12V电源不受此开关控制.
4、输出端和输入端一一对应,当输入端为5V时,输出端也为高电平,输入端为低电平时输出亦为低电平。

电源管理模块
3.2.1 智能车电源设计要点
电源是整个系统稳定工作的前提,因此必须有一个合理的电源设计,对于小车来说电源设计应
注意两点:
1. 与一般的稳压电源不同,小车的电池电压一般在6-8V 左右,还要考虑在电池损耗的情况下电压的降低,因此常用的78 系列稳压芯片不再能够满足要求,因此必须采用低压差的稳压芯片,在本文中以较为常见的LM2940-5.0 为例。
2. 单片机必须与大电流器件分开供电,避免大电流器件对单片机造成干扰,影响单片机的稳定运行。
现在各种新型的电源芯片层出不穷,各位读者可以根据自己的需求自行选择电源芯片,对于本设计应该主要注意稳压压差和最大输出电流两个指标能否满足设计要求。
3.2.2 低压差稳压芯片LM2940 简介
LM2940 系列是输出电压固定的低压差三端端稳压器;输出电压有5V、8V、10V 多种;最大输出电流1A;输出电流1A 时,最小输入输出电压差小于0.8V;最大输入电压26V;工作温度-40~+125℃;
内含静态电流降低电路、电流限制、过热保护、电池反接和反插入保护电路。同时LM2940 价格适中而且较容易购买,非常适合在本设计中使用。LM2940-5.0 封装和实物如图3.4所示。
如图3.5所示,采用两路供电,这样可以使用其中一路单独为STM32F4discovery电路板供电,指示灯等供电。另外一路提供L298N、舵机、蓝牙的工作电压,L298N 的驱动电压由电池不经任何处理直接给出。舵机可以用6V 供电,也可以直接用5V 供电。

PCB图

图3.7 是运用腐蚀液自制的电源管理模块,具有12v\5v的供电模块,可同时给电机模块和主控板(STM32DISCOVERY)供电:

4、系统软件设计
4.1 主控板程序设计
4.1.1 main程序设计
主函数主要分为延迟时间初始化、串口接收模块程序、电机初始化三部分。主函数的流程图如图4.1所示:

主函数程序代码如下:
int main(void){ delay_init(168);//延迟时间初始化uart_init(9600);//串口初始化Direction(1);//电机初始化delay_ms(10);//延迟10msprintf(" welcome to control the smart car!:\n\r");//输入语句while(1);}
4.1.2 串口接收模块程序
主控板接收到蓝牙从串口传来的数据后存入Res变量,然后通过分支程序来选择执行前进、后退、左转、右转和停止等功能。该模块的程序流程图如图2所示:

4.1.3 修改PWM输出值程序
该程序是基于“4_PWM的实现”中的程序改编的。代码如下:
void Change_PWM(int duty1,int duty2,int duty3,int duty4)
{SCB->AIRCR=0x05AF00;// 中断优先级分组 抢占:响应=3:1 RCC->AHB1ENR|=(1<<2);// 打开GPIOC时钟GPIOC->MODER|=0x000AA000;// pc6789第二功能,推挽输出GPIOC->OSPEEDR|=0x000FF000;//输出速度为100mGPIOC->PUPDR|=0x00055000;//上拉GPIOC->AFR[0]|=0x22000000;//pc6789的第二功能为AF2GPIOC->AFR[1]|=0x00000022;RCC->APB1ENR|=(1<<1);//打开TIM3时钟TIM3->PSC=83;//对84M时钟进行84分频,使得计数频率为1MTIM3->ARR=10000;//周期为10msTIM3->EGR|=1;//产生一次更新时间TIM3->CCMR1|=0x6060;//PWM模式1TIM3->CCMR2|=0x6060;//PWM模式2TIM3->CCR1=duty1;//1路PWMTIM3->CCR2=duty2;//2路PWMTIM3->CCR3=duty3;//3路PWMTIM3->CCR4=duty4;//4路PWMTIM3->CCER|=0x1111;//使能比较输出TIM3->CCMR1|=0x0808;//启动预装载TIM3->CCMR2|=0x8080;TIM3->CR1|=1;//开始计时
}
4.1.4 设置电机转向程序
改程序将电机驱动模块的8个输入端口接到了主控板的8个GPIO口,通过推挽输出,从而控制电机的转向,代码如下:
void Direction(int direction)
{
SysTick_Config(SystemCoreClock / 1000); //时钟中断设为1msRCC->AHB1ENR |= 0x00000005; //使能GPIOA和GPIOD时钟RCC->APB2ENR |= (1<<14); //使能syscfg时钟
if(direction==0)
{GPIOA->MODER &= 0xffff0000; //设置PA0,1,2,3为输出GPIOA->MODER |= 0x00005555; GPIOA->OTYPER &= 0xFFFFff00; //设置PA0,1,2,3为推挽输出GPIOA->OSPEEDR &= 0xffff0000; //设置PA0,1,2,3的输出速度为100MGPIOA->OSPEEDR |= 0x0000ffff;SYSCFG->CMPCR = 0x00000001; //使用IO补偿单元GPIOA->PUPDR &= 0xffffff00; //设置PA0,1,2,3无上拉,无下拉GPIOA->BSRRH = 0x00ff; //复位GPIOA_BSRRH寄存器GPIOA->BSRRL = 0x0055; }else{
GPIOA->MODER &= 0xffff0000; //设置PA0,1,2,3为输出GPIOA->MODER |= 0x0000005555; GPIOA->OTYPER &= 0xFFFFff00; //设置PA0,1,2,3为推挽输出GPIOA->OSPEEDR &= 0xffff0000; //设置PA0,1,2,3的输出速度为100MGPIOA->OSPEEDR |= 0x0000ffff;SYSCFG->CMPCR = 0x00000001; //使用IO补偿单元GPIOA->PUPDR &= 0xffffff00; //设置PA0,1,2,3无上拉,无下拉GPIOA->BSRRH = 0x00ff; //复位GPIOA_BSRRH寄存器GPIOA->BSRRL = 0x00AA;
}
}
4.2 android客户端程序设计
4.2.1 控制界面的布局
控制界面主要运用了线性布局、相对布局和表格布局。整体采用线性布局,局部采用相对布局,而控制按钮采用了表格布局。控制界面的布局如图4.1所示:

4.2.2 布局的代码如下:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="wrap_content"android:layout_height="fill_parent"android:orientation="vertical" >
<RelativeLayout android:id = "@+id/container"android:orientation="vertical"android:layout_width="fill_parent"android:layout_height="fill_parent"><RelativeLayoutandroid:layout_width="fill_parent"android:layout_height="wrap_content"android:id= "@+id/edit_bottombar"android:layout_alignParentBottom = "true"><Button android:id="@+id/btn_disconnect"android:layout_width="65dp" android:layout_height="wrap_content" android:layout_alignParentLeft ="true"android:text="断开"/> <Button android:id="@+id/btn_msg_send"android:layout_width="65dp" android:layout_height="wrap_content" android:layout_alignParentRight ="true"android:text="发送"/> <EditTextandroid:id="@+id/MessageText"android:layout_width="98dp"android:layout_height="wrap_content"android:layout_toRightOf="@+id/btn_disconnect"android:hint="说点什么呢?"android:textSize="15dip"/></RelativeLayout> <ListViewandroid:id="@+id/list"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_above="@id/edit_bottombar"android:layout_below="@id/container"android:layout_weight="1.0"android:divider="#ffc6c6c6"android:scrollingCache="false"android:visibility="visible" /><TableLayoutandroid:layout_width="match_parent"android:layout_height="wrap_content" ><TableRowandroid:id="@+id/tableRow1"android:layout_width="wrap_content"android:layout_height="wrap_content" ><Buttonandroid:id="@+id/button1"android:layout_width="wrap_content"android:layout_height="wrap_content"android:text="Button"android:visibility="invisible" /><Buttonandroid:id="@+id/start"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_marginRight="0dp"android:text="start"android:width="120px" /></TableRow><TableRowandroid:id="@+id/tableRow2"android:layout_width="wrap_content"android:layout_height="0dp"android:layout_weight="1" ><Buttonandroid:id="@+id/left"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_marginRight="0dp"android:text="left"android:width="120px" /><Buttonandroid:id="@+id/stop"android:layout_width="wrap_content"android:layout_height="wrap_content"android:text="stop"android:width="120px" /><Buttonandroid:id="@+id/right"android:layout_width="wrap_content"android:layout_height="wrap_content"android:text="right"android:width="120px" /></TableRow><TableRowandroid:id="@+id/tableRow3"android:layout_width="wrap_content"android:layout_height="wrap_content" ><Buttonandroid:id="@+id/button2"android:layout_width="wrap_content"android:layout_height="wrap_content"android:text="Button"android:visibility="invisible" /><Buttonandroid:id="@+id/back"android:layout_width="wrap_content"android:layout_height="wrap_content"android:text="Back" /></TableRow></TableLayout>
</RelativeLayout>
</LinearLayout>
4.2.3 android客户端的界面如图4.2所示:

4.2.4 发送按钮的代码
sendButton= (Button)findViewById(R.id.btn_msg_send);
sendButton.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View arg0) {
// TODO Auto-generated method stub
String msgText =editMsgView.getText().toString();//获取编辑框内的内容
if (msgText.length()>0) {sendMessageHandle(msgText);//发送编辑框的内容给串口 editMsgView.setText("");//清空编辑框editMsgView.clearFocus();
//close InputMethodManager
InputMethodManager imm = (InputMethodManager)getSystemService(Context.INPUT_METHOD_SERVICE); imm.hideSoftInputFromWindow(editMsgView.getWindowToken(), 0);
}
else
Toast.makeText(mContext, "发送内容不能为空!", Toast.LENGTH_SHORT).show();
}
});
4.2.5 控制按钮的代码
以左转按钮为例:
sendButton= (Button)findViewById(R.id.left);
sendButton.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View arg0) {
String msgText ="1"; // 发送左转命令“l”
if (msgText.length()>0) {
sendMessageHandle(msgText);//发送“l”给串口
editMsgView.setText("");//清空编辑框
editMsgView.clearFocus();
//close InputMethodManager
InputMethodManager imm = (InputMethodManager)getSystemService(Context.INPUT_METHOD_SERVICE);
imm.hideSoftInputFromWindow(editMsgView.getWindowToken(), 0);
}
else
Toast.makeText(mContext, "发送内容不能为空!", Toast.LENGTH_SHORT).show();
}
});
5、系统创新
自从有了智能手机,机器人的应用也就多了一个新的方向:手机控制机器人。2005年日本第二大电信运营商KDDI和机械制造商I Bee KK联合推出了第一款手机控制机器人。当然了,想要操纵这种机器人,你首先需要使用KDDI网络,并且用户的手机上,本身还需要带有蓝牙功能,然后再通过KDDI提供BREW方式下载机器人的驱动程式和控制系统。不过这种机器人的价格却相对高昂,预售市价约合人民币15000元。随着Android系统技术的普及,可以做个基于Android的客户端,在小车上装个接收蓝牙信号的FBT蓝牙接收模块,然后就可以通过客户端发送蓝牙信号,来对蓝牙小车进行控制控制,其接收可达15米,完全能适应对小车的要求。其中这个FBT蓝牙接收模块是低耗能,这样就把更多的能量用在小车的驱动上。
小车需要很大的马力和很好的灵活性以应对不同的地形。这辆车的车轮使用四驱的直流电机来驱动的,用PWM波来控制小车的速度,可以很方便的更改其速度,有主控板通过推挽输出来控制电机的翻转以让车子进行后退的速度。这样就可以胜任对小车的要求。
6、评测与结论
首先,给电源模块上12v的电源,然后打开电机驱动模块开关,同时将主控板的供电端连接到电源管理模块。然后,在android手机上安装“蓝牙通信”应用程序后,打开该APP,然后选择“允许打开蓝牙”。点击设备列表中的“开始搜索按钮”,在设备列表中选择蓝牙模块的名字进行连接。
完成上述工作以后,就可以在手机上通过按下“start”、“left”、“stop”、“right”、“back”通过蓝牙给小车发送“前进”、“左转”、“停止”、“右转”、“后退”5个命令。小车可以解析命令轻松进行前进、后退、左转、右转和停止。


相关文章:
基于stm32f4的蓝牙控制小车
1. 引言 蓝牙的创始人是瑞典爱立信公司,蓝牙技术是一种无限数据与语音通信的开放性全球规范,它以低成本的近距离无线连接为基础,为固定与移动设备通信环境建立一个特别连接。手机之间通过蓝牙实现数据共享成为常理,将手机变为遥…...
基于BP神经网络的租金预测
目录 摘要 BP神经网络参数设置及各种函数选择 参数设置 训练函数 传递函数 学习函数 性能函数 显示函数 前向网络创建函数 BP神经网络训练窗口详解 训练窗口例样 训练窗口四部详解 基于BP神经网络的租金预测 代码下载:基于BP神经网络的租金预测(代码完整,数据齐全)资源-CS…...
C语言学习记录—进阶作业(通讯录文件版本)
通讯录 1. 添加一个函数,在退出通讯录的时候把信息到保存到文件中 2. 添加一个函数,在通讯录打开的时候,可以把文件中的信息加载到通讯录中 contact.h文件 #pragma once #include <string.h> #include <stdio.h> #include <…...
深度学习笔记(四)——TF2构建基础网络常用函数+简单ML分类网络实现
文中程序以Tensorflow-2.6.0为例 部分概念包含笔者个人理解,如有遗漏或错误,欢迎评论或私信指正。 截图和程序部分引用自北京大学机器学习公开课 TF2基础常用函数 1、张量处理类 强制数据类型转换: a1 tf.constant([1,2,3], dtypetf.floa…...
GPT function calling v2
原文:GPT function calling v2 - 知乎 OpenAI在2023年11月10号举行了第一次开发者大会(OpenAI DevDays),其中介绍了很多新奇有趣的新功能和新应用,而且更新了一波GPT的API,在1.0版本后的API调用与之前的0.…...
【Golang】IEEE754标准二进制字符串转为浮点类型
IEEE754介绍 IEEE 754是一种标准,用于表示和执行浮点数运算的方法。在这个标准中,单精度浮点数使用32位二进制表示,分为三个部分:符号位、指数位和尾数位。 符号位(s)用一个位来表示数的正负,0表示正数,1表…...
【开源项目】轻量元数据管理解决方案——Marquez
大家好,我是独孤风。 又到了本周的开源项目推荐。最近推荐的元数据管理项目很多,但是很多元数据管理平台的功能复杂难用。 那么有没有轻量一点的元数据管理项目呢? 今天为大家推荐的开源项目,就是一个轻量级的元数据管理工具。虽然…...
dirty file page
转自:https://www.cnblogs.com/zhiminyu/p/17330763.html 0.前言 Linux 内核Page Cache 和Buffer Cache 关系及演化历史 一文中讲过Linux 2.4之后将Page Cache和Buffer Cache 进行了融合,在buffer_head 中添加了b_page,很容易就能找到缓存的…...
HTAP(Hybrid Transactional/Analytical Processing)系统之统一存储的实时之道
文章目录 HTAP与时俱进LASER中的存储关键知识LSM(Log-Structured Merge Tree)SkipList(跳表)CDC(Changed Data Capture)SST(Sorted Sequence Table) 特性列组(Column Gro…...
【linux】tcpdump 使用
tcpdump 是一个强大的网络分析工具,可以在 UNIX 和类 UNIX 系统上使用,用于捕获和分析网络流量。它允许用户截取和显示发送或接收过网络的 TCP/IP 和其他数据包。 一、安装 tcpdump 通常是默认安装在大多数 Linux 发行版中的。如果未安装,可…...
数字图像处理常用算法的原理和代码实现详解
本专栏详细地分析了常用图像处理算法的数学原理、实现步骤。配有matlab或C实现代码,并对代码进行了详细的注释。最后,对算法的效果进行了测试。相信通过这个专栏,你可以对这些算法的原理及实现有深入的理解! 如有疑问…...
Pandas实战100例 | 案例 26: 检测异常值
案例 26: 检测异常值 知识点讲解 在数据分析中,检测和处理异常值(或离群值)是一个重要的步骤。异常值可能会影响数据的整体分析。一种常用的方法是使用四分位数和四分位数间距(IQR)来识别异常值。 四分位数和 IQR: …...
C语言学习NO.11-字符函数strlen,strlen函数的使用,与三种strlen函数的模拟实现
(一)strlen函数的使用 strlen函数的演示 #include <stdio.h> #include <string.h>int main() {char arr1[] "abcdef";char arr2[] "good";printf("arr1 %d,arr2 %d",strlen(arr1),strlen(arr2));return …...
Vue3+ts获取props的值并且定义props值的类型的方法。
1.引入withDefaults模块,给defineProps绑定默认值。 import { withDefaults } from vue2.定义Props传输值的类型。 interface Props {// 类型type: string;name: string;id: number; }3.给props的值设置默认值。 const props withDefaults(defineProps<Prop…...
EasyExcel 不使用科学计数发并以千分位展示
EasyExcel 不使用科学计数发并以千分位展示 不使用科学计数法 不使用科学计数法 BigDecimalStringConverter 将 BigDecimal 类型的数值转换为字符串类型,并将其导出到 Excel 文件中。在 convertToExcelData 方法中,我们将 BigDecimal 转换为字符串&…...
【Python机器学习】SVM——调参
下面是支持向量机一个二维二分类数据集的训练结果: import mglearn import matplotlib.pyplot as plt from sklearn.svm import SVCplt.rcParams[font.sans-serif] [SimHei] plt.rcParams[axes.unicode_minus] False X,ymglearn.tools.make_handcrafted_dataset()…...
网络传输(TCP)
前言 我们tcpdump抓包时会看到除报文数据外,前面还有一段其他的数据,这段数据分为两部分,ip包头(一般20字节)和tcp包头(一般20字节),一般这两个头长度和为40,我们直接跳…...
MFC模拟消息发送,自定义以及系统消息
在MFC框架下,有很多系统已经定义好的消息,例如ON_WM_LBUTTONDOWN()、ON_WM_MBUTTONDOWN()等等。我们在使用的时候只需要声明并调用就可以了,最简单的用法。 提升了一点难度的用法就是自己设置自定义消息,再提升一点难度的就是如何…...
并发,并行,线程与UI操作
并行和并发是计算机领域中两个相关但不同的概念。 并行(Parallel)指的是同时执行多个任务或操作,它依赖于具有多个处理单元的系统。在并行计算中,任务被分成多个子任务,并且这些子任务可以同时在不同的处理单元上执行…...
react 6种方式编写样式
在React中,编写样式主要有以下几种方式: 1. 内联样式: 直接在React组件中使用style属性来定义样式。这种方式比较适合定义动态的样式,因为它允许你将JavaScript表达式作为样式的值。 2. 外部样式表 :通过创建外部的…...
从零实现STL哈希容器:unordered_map/unordered_set封装详解
本篇文章是对C学习的STL哈希容器自主实现部分的学习分享 希望也能为你带来些帮助~ 那咱们废话不多说,直接开始吧! 一、源码结构分析 1. SGISTL30实现剖析 // hash_set核心结构 template <class Value, class HashFcn, ...> class hash_set {ty…...
三体问题详解
从物理学角度,三体问题之所以不稳定,是因为三个天体在万有引力作用下相互作用,形成一个非线性耦合系统。我们可以从牛顿经典力学出发,列出具体的运动方程,并说明为何这个系统本质上是混沌的,无法得到一般解…...
JUC笔记(上)-复习 涉及死锁 volatile synchronized CAS 原子操作
一、上下文切换 即使单核CPU也可以进行多线程执行代码,CPU会给每个线程分配CPU时间片来实现这个机制。时间片非常短,所以CPU会不断地切换线程执行,从而让我们感觉多个线程是同时执行的。时间片一般是十几毫秒(ms)。通过时间片分配算法执行。…...
根据万维钢·精英日课6的内容,使用AI(2025)可以参考以下方法:
根据万维钢精英日课6的内容,使用AI(2025)可以参考以下方法: 四个洞见 模型已经比人聪明:以ChatGPT o3为代表的AI非常强大,能运用高级理论解释道理、引用最新学术论文,生成对顶尖科学家都有用的…...
【Nginx】使用 Nginx+Lua 实现基于 IP 的访问频率限制
使用 NginxLua 实现基于 IP 的访问频率限制 在高并发场景下,限制某个 IP 的访问频率是非常重要的,可以有效防止恶意攻击或错误配置导致的服务宕机。以下是一个详细的实现方案,使用 Nginx 和 Lua 脚本结合 Redis 来实现基于 IP 的访问频率限制…...
【Linux】Linux安装并配置RabbitMQ
目录 1. 安装 Erlang 2. 安装 RabbitMQ 2.1.添加 RabbitMQ 仓库 2.2.安装 RabbitMQ 3.配置 3.1.启动和管理服务 4. 访问管理界面 5.安装问题 6.修改密码 7.修改端口 7.1.找到文件 7.2.修改文件 1. 安装 Erlang 由于 RabbitMQ 是用 Erlang 编写的,需要先安…...
TCP/IP 网络编程 | 服务端 客户端的封装
设计模式 文章目录 设计模式一、socket.h 接口(interface)二、socket.cpp 实现(implementation)三、server.cpp 使用封装(main 函数)四、client.cpp 使用封装(main 函数)五、退出方法…...
JUC并发编程(二)Monitor/自旋/轻量级/锁膨胀/wait/notify/锁消除
目录 一 基础 1 概念 2 卖票问题 3 转账问题 二 锁机制与优化策略 0 Monitor 1 轻量级锁 2 锁膨胀 3 自旋 4 偏向锁 5 锁消除 6 wait /notify 7 sleep与wait的对比 8 join原理 一 基础 1 概念 临界区 一段代码块内如果存在对共享资源的多线程读写操作…...
【QT】qtdesigner中将控件提升为自定义控件后,css设置样式不生效(已解决,图文详情)
目录 0.背景 1.解决思路 2.详细代码 0.背景 实际项目中遇到的问题,描述如下: 我在qtdesigner用界面拖了一个QTableView控件,object name为【tableView_electrode】,然后【提升为】了自定义的类【Steer_Electrode_Table】&…...
Monorepo架构: 项目管理模式对比与考量
关于 monorepo 相关概念及项目管理模式 在软件开发中,尤其是前端项目,我们会涉及到不同的项目管理模式,这里先介绍几个重要的概念“monorepo”是当前较为热门的一种项目管理方式,虽然很多人可能听说过,但可能在实际项…...
