QT串口助手
- 成品展示
- QT全部程序构成
- zua.pro
- serial.h
- main.cpp
- serial.cpp
- serial.ui
- Keil全部程序构成
- main.c
- stm32f10x_conf.h
- stm32f10x_it.c
- 5.stm32f10x_it.h
- bsp_usart.c
- bsp_led.c
- bsp_exit.c
- bsp_dht11.c
- bsp_delay.c
- 介绍
- 硬件
- 野火F103指南者+DHT11温湿度传感器
- QT全部程序构成
- QT设计的思路
- Keil全部程序构成
- Keil程序设计的思路
- 功能
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 全部功能
- BUG解决方法
成品展示
其他功能展示在文章 最后
QT全部程序构成
zua.pro
只添加了 QT += core gui serialport
RC_ICONS =system.ico
QT += core gui serialportgreaterThan(QT_MAJOR_VERSION, 4): QT += widgetsCONFIG += c++11DEFINES += QT_DEPRECATED_WARNINGSSOURCES += \main.cpp \serial.cppHEADERS += \serial.hFORMS += \serial.uiRC_ICONS =system.icoqnx: target.path = /tmp/$${TARGET}/bin
else: unix:!android: target.path = /opt/$${TARGET}/bin
!isEmpty(target.path): INSTALLS += targetRESOURCES += \zua.qrc
serial.h
#ifndef SERIAL_H
#define SERIAL_H#include <QMainWindow>
/*---port串口--*/
#include <QSerialPort>
#include <QSerialPortInfo>
/*---QString--*/
#include <QString>
/*---QTimer定时器--*/
#include <QTimer>
/*-----QToolBar工具栏------*/
#include <QToolBar>
/*---QDebug打印--*/
#include <QDebug>QT_BEGIN_NAMESPACE
namespace Ui { class Serial; }
QT_END_NAMESPACEclass Serial : public QMainWindow
{Q_OBJECTpublic:Serial(QWidget *parent = nullptr);~Serial();private:Ui::Serial *ui;/*------------变量----------------*/QSerialPort mSerialPort;//串口全局类声明bool mIsOpen;//是否打开串口QTimer *timer;//自动发送定时器声明/*------------函数----------------*/bool getSerialPortConfig();// 获取串口配置 打开串口//用户系统初始化void sysIint();//字符串转16进制QByteArray QString2Hex(QString str);//字符转16进制char ConvertHexChar(char ch);signals: //自定义信号//发送使能信号void my_send_signals(bool); //触发发送信号private slots:void on_SerialPort_readyRead();// 准备接收串口数据槽,自定义的void on_btnOpen_clicked();//打开串口按键槽,使用转到槽void on_btnClose_clicked();//关闭串口按键,使用转到槽void on_BtnSend_clicked();//发送按键,使用转到槽void on_btn_clear_clicked();//清楚接收区按键,使用转到槽void on_btn_clear_send_clicked();//清空发送区按键,使用转到槽void on_checkBox_3_stateChanged(int arg1);//自动触发复选框 启动定时器和停止定时器,使用转到槽
};
#endif // SERIAL_H
main.cpp
没有改变
serial.cpp
#include "serial.h"
#include "ui_serial.h"Serial::Serial(QWidget *parent): QMainWindow(parent), ui(new Ui::Serial)
{ui->setupUi(this);sysIint();
}Serial::~Serial()
{delete ui;
}
// 获取串口配置 打开串口
bool Serial::getSerialPortConfig()
{// 获取串口配置QString mPortName = ui->cmd_port_name->currentText();// 端口号QString mBaudRate = ui->cmd_baud_rate->currentText();// 波特率QString mDataBits = ui->cmd_Data_Bits->currentText();// 数据位QString mParity = ui->cmd_parity->currentText();// 校验位QString mStopBits = ui->cmd_Stop_Bits->currentText();// 停止位// 设置串口// 端口号mSerialPort.setPortName(mPortName);// 波特率mSerialPort.setBaudRate(mBaudRate.toInt());/*if("9600" == mBaudRate){mSerialPort.setBaudRate(QSerialPort::Baud9600);}else if("115200" == mBaudRate){mSerialPort.setBaudRate(QSerialPort::Baud115200);}else{mSerialPort.setBaudRate(QSerialPort::Baud19200);}*/// 数据位if("5" == mDataBits){mSerialPort.setDataBits(QSerialPort::Data5);}else if("6" == mDataBits){mSerialPort.setDataBits(QSerialPort::Data6);}else if("7" == mDataBits){mSerialPort.setDataBits(QSerialPort::Data7);}else{mSerialPort.setDataBits(QSerialPort::Data8);}// 校验位if("Even" == mParity){mSerialPort.setParity(QSerialPort::EvenParity);// 偶数}else if ("ODD" == mParity){mSerialPort.setParity(QSerialPort::OddParity);//奇数的}else{mSerialPort.setParity(QSerialPort::NoParity);}// 停止位if("1.5" == mStopBits){mSerialPort.setStopBits(QSerialPort::OneAndHalfStop);}else if("2" == mStopBits){mSerialPort.setStopBits(QSerialPort::TwoStop);}else{mSerialPort.setStopBits(QSerialPort::OneStop);}// 打开串口return mSerialPort.open(QSerialPort::ReadWrite);
}
//系统初始化
void Serial::sysIint()
{ui->cmd_port_name->clear();this->setWindowTitle("xxx的串口调试GUI");//窗口名字connect(&mSerialPort,&QSerialPort::readyRead,this,&Serial::on_SerialPort_readyRead);//XImIsOpen = false;ui->BtnSend->setEnabled(mIsOpen);//没连接串口按键为灰色// 智能识别当前系统的有效串口号//通过QSerialPortInfo查找可用串口foreach(const QSerialPortInfo &info, QSerialPortInfo::availablePorts()){//将可用串口添加到端口显示框ui->cmd_port_name->addItem(info.portName());}//timer 自动发送timer = new QTimer(this);connect(timer,&QTimer::timeout,[=](){emit my_send_signals(true); //触发发送信号});//QToolBar *toolBar1= addToolBar("工具栏");//QToolBar工具栏//发送信号 发送槽函数connect(this,&Serial::my_send_signals,this,&Serial::on_BtnSend_clicked);
}
//字符串转16进制
QByteArray Serial::QString2Hex(QString str)
{QByteArray senddata;int hexdata,lowhexdata;int hexdatalen = 0;int len = str.length();senddata.resize(len/2);char lstr,hstr;for(int i=0; i<len; ){hstr=str[i].toLatin1();if(hstr == ' '){i++;continue;}i++;if(i >= len)break;lstr = str[i].toLatin1();hexdata = ConvertHexChar(hstr);lowhexdata = ConvertHexChar(lstr);if((hexdata == 16) || (lowhexdata == 16))break;elsehexdata = hexdata*16+lowhexdata;i++;senddata[hexdatalen] = (char)hexdata;hexdatalen++;}senddata.resize(hexdatalen);return senddata;
}
//字符转16进制
char Serial::ConvertHexChar(char ch)
{if((ch >= '0') && (ch <= '9'))return ch-0x30;else if((ch >= 'A') && (ch <= 'F'))return ch-'A'+10;else if((ch >= 'a') && (ch <= 'f'))return ch-'a'+10;else return (-1);
}/*-----------------slot--------------------*/
// 准备接收串口数据槽
void Serial::on_SerialPort_readyRead()
{QByteArray recvData = mSerialPort.readAll();//从串口读取数据QString str_rev;if(ui->chk_rev_hex->checkState() == Qt::Checked){ //HEX 16进制str_rev = QString(recvData.toHex(' ').toUpper().append(' '));}else{str_rev =QString::fromLocal8Bit(recvData);//处理汉语显示乱码,函数返回的是String类型的数}//文本框显示接收数据ui->textRecv->insertPlainText(str_rev);//显示内容,不会自动清空//将光标定位到最后ui->textRecv->moveCursor(QTextCursor::End);
}//打开串口按键
void Serial::on_btnOpen_clicked()
{if(true == mIsOpen){// 当前串口已经打开了一个串口,这时要执行关闭串口mSerialPort.close();mIsOpen = false;ui->BtnSend->setEnabled(mIsOpen);//发送按键不用}else{// 当前串口助手没有打开串口,这时要执行打开串口动作if(true == getSerialPortConfig()){mIsOpen = true;ui->BtnSend->setEnabled(mIsOpen);//发送按键可用}else{mIsOpen = false;}}
}
//关闭串口按键
void Serial::on_btnClose_clicked()
{mSerialPort.close();//执行关闭串口ui->BtnSend->setEnabled(false);//没连接串口按键为灰色mIsOpen = false;ui->textRecv->clear();//关闭串口,同时清楚接收区
}//发送按键
void Serial::on_BtnSend_clicked()
{qDebug()<<"发送按键"<<endl;QString data = ui->textSend->toPlainText();QByteArray array;//字节数组//复选框 16 进制发送if(ui->chk_send_hex->checkState() == Qt::Checked){array = QString2Hex(data); //HEX 16进制,QString转Hex QString转十六进制数,自己写的}else{array = data.toLatin1(); //ASCII,QString类提供的}mSerialPort.write(array); //发送数据//发送完后,清空发送区ui->textSend->clear();
}
//清楚接收区按键
void Serial::on_btn_clear_clicked()
{qDebug()<<"清楚接收区按键"<<endl;ui->textRecv->clear();
}
//清空发送区按键
void Serial::on_btn_clear_send_clicked()
{qDebug()<<"清空发送区按键"<<endl;ui->textSend->clear();
}
//自动触发复选框 启动定时器和停止定时器
void Serial::on_checkBox_3_stateChanged(int arg1)
{if(arg1){timer->start(ui->spinBox->value()); //启动定时器}else{timer->stop(); //停止定时器}
}
槽函数slot 区分
serial.ui
- 左侧组QGroupBox
- 右侧组QGroupBox
Keil全部程序构成
main.c
#include "stm32f10x.h"
#include "./led/bsp_led.h"
#include "./usart/bsp_usart.h"
#include "./Key/bsp_exti.h"
#include "./dht11/bsp_dht11.h"static void Show_Message(void);int main(void)
{ char ch;/* 初始化EXTI中断,按下按键会触发中断,* 触发中断会进入stm32f4xx_it.c文件中的函数* KEY1_IRQHandler和KEY2_IRQHandler,处理中断,反转LED灯,发送学号姓名。*/EXTI_Key_Config(); /*初始化DTT11的引脚*/DHT11_Init ();/* 初始化RGB彩灯 */LED_GPIO_Config();/* 初始化USART 配置模式为 115200 8-N-1 */USART_Config();LED1_ON;/* 打印指令输入提示信息 */Show_Message();while(1){ /* 获取字符指令 */ch=getchar();printf("接收到字符:%c\n",ch);/* 根据字符指令控制RGB彩灯颜色 */switch(ch){case '1':LED_RED;break;case '2':LED_GREEN;break;case '3':LED_BLUE;break;case '4':LED_YELLOW;break;case '5':LED_PURPLE;break;case '6':LED_CYAN;break;case '7':LED_WHITE;break;case '8':LED_RGBOFF;break;default:/* 如果不是指定指令字符,打印提示信息 */Show_Message();break; } }
}/* 打印指令输入提示信息*/
static void Show_Message(void)
{printf("\r\r这是一个通过串口通信指令控制RGB彩灯实验 \n");printf("使用 USART 参数为:%d 8-N-1 \n",DEBUG_USART_BAUDRATE);printf("开发板接到指令后控制RGB彩灯颜色\n");printf("指令对应如下:\n");printf(" 指令 ------ 彩灯颜色 \n");printf(" 1 ------ 红 \n");printf(" 2 ------ 绿 \n");printf(" 3 ------ 蓝 \n");printf(" 4 ------ 黄 \n");printf(" 5 ------ 紫 \n");printf(" 6 ------ 青 \n");printf(" 7 ------ 白 \n");printf(" 8 ------ 灭 \n");
}
stm32f10x_conf.h
注意文件中 有可能会 注释掉了一些头文件,把注释去掉就好啦
stm32f10x_it.c
在最后插入以下
void KEY1_IRQHandler(void)
{//确保是否产生了EXTI Line中断if(EXTI_GetITStatus(KEY1_INT_EXTI_LINE) != RESET) {// LED1 取反 LED1_TOGGLE;printf("\r ---学号---姓名--\n");/* 发送学号、姓名*/Usart_SendString(DEBUG_USARTx,"191xxxxxx7-xxx\n");Usart_SendString(DEBUG_USARTx,"191xxxxxx0李x\n");Usart_SendString(DEBUG_USARTx,"190xxxxxx3周xx\n");Usart_SendString(DEBUG_USARTx,"191xxxxxx5卢xx\n"); //清除中断标志位EXTI_ClearITPendingBit(KEY1_INT_EXTI_LINE); }
}void KEY2_IRQHandler(void)
{DHT11_Data_TypeDef DHT11_Data;//确保是否产生了EXTI Line中断if(EXTI_GetITStatus(KEY2_INT_EXTI_LINE) != RESET) {// LED2 取反 LED2_TOGGLE;/*调用DHT11_Read_TempAndHumidity读取温湿度,若成功则输出该信息*/if( DHT11_Read_TempAndHumidity ( & DHT11_Data ) == SUCCESS){printf("\r\n读取DHT11成功!\r\n\r\n湿度为%d.%d %RH ,温度为 %d.%d℃ \r\n",\DHT11_Data.humi_int,DHT11_Data.humi_deci,DHT11_Data.temp_int,DHT11_Data.temp_deci);} else{printf("Read DHT11 ERROR!\r\n");}//清除中断标志位EXTI_ClearITPendingBit(KEY2_INT_EXTI_LINE); }
}
5.stm32f10x_it.h
添加了头文件,其他保留
#include "stm32f10x.h"
#include "./led/bsp_led.h"
#include "./usart/bsp_usart.h"
#include "./Key/bsp_exti.h"
#include "./dht11/bsp_dht11.h"
bsp_usart.c
#include "bsp_usart.h"void USART_Config(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
USART_InitTypeDef USART_InitStructure;// 打开串口GPIO的时钟
DEBUG_USART_GPIO_APBxClkCmd(DEBUG_USART_GPIO_CLK, ENABLE);// 打开串口外设的时钟
DEBUG_USART_APBxClkCmd(DEBUG_USART_CLK, ENABLE);// 将USART Tx的GPIO配置为推挽复用模式
GPIO_InitStructure.GPIO_Pin = DEBUG_USART_TX_GPIO_PIN;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(DEBUG_USART_TX_GPIO_PORT, &GPIO_InitStructure);// 将USART Rx的GPIO配置为浮空输入模式
GPIO_InitStructure.GPIO_Pin = DEBUG_USART_RX_GPIO_PIN;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_Init(DEBUG_USART_RX_GPIO_PORT, &GPIO_InitStructure);// 配置串口的工作参数
// 配置波特率
USART_InitStructure.USART_BaudRate = DEBUG_USART_BAUDRATE;
// 配置 针数据字长
USART_InitStructure.USART_WordLength = USART_WordLength_8b;
// 配置停止位
USART_InitStructure.USART_StopBits = USART_StopBits_1;
// 配置校验位
USART_InitStructure.USART_Parity = USART_Parity_No ;
// 配置硬件流控制
USART_InitStructure.USART_HardwareFlowControl =
USART_HardwareFlowControl_None;
// 配置工作模式,收发一起
USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
// 完成串口的初始化配置
USART_Init(DEBUG_USARTx, &USART_InitStructure);// 使能串口
USART_Cmd(DEBUG_USARTx, ENABLE);
}/***************** 发送一个字符 **********************/
void Usart_SendByte( USART_TypeDef * pUSARTx, uint8_t ch)
{
/* 发送一个字节数据到USART */
USART_SendData(pUSARTx,ch);/* 等待发送数据寄存器为空 */
while (USART_GetFlagStatus(pUSARTx, USART_FLAG_TXE) == RESET);
}/***************** 发送字符串 **********************/
void Usart_SendString( USART_TypeDef * pUSARTx, char *str)
{
unsigned int k=0;do {Usart_SendByte( pUSARTx, *(str + k) );k++;} while(*(str + k)!='\0');/* 等待发送完成 */while(USART_GetFlagStatus(pUSARTx,USART_FLAG_TC)==RESET){}
}/***************** 发送一个16位数 **********************/
void Usart_SendHalfWord( USART_TypeDef * pUSARTx, uint16_t ch)
{
uint8_t temp_h, temp_l;/* 取出高八位 */
temp_h = (ch&0XFF00)>>8;
/* 取出低八位 */
temp_l = ch&0XFF;/* 发送高八位 */
USART_SendData(pUSARTx,temp_h);
while (USART_GetFlagStatus(pUSARTx, USART_FLAG_TXE) == RESET);/* 发送低八位 */
USART_SendData(pUSARTx,temp_l);
while (USART_GetFlagStatus(pUSARTx, USART_FLAG_TXE) == RESET);
}///重定向c库函数printf到串口,重定向后可使用printf函数
int fputc(int ch, FILE *f)
{
/* 发送一个字节数据到串口 */
USART_SendData(DEBUG_USARTx, (uint8_t) ch);/* 等待发送完毕 */
while (USART_GetFlagStatus(DEBUG_USARTx, USART_FLAG_TXE) == RESET);return (ch);
}///重定向c库函数scanf到串口,重写向后可使用scanf、getchar等函数
int fgetc(FILE *f)
{
/* 等待串口输入数据 */
while (USART_GetFlagStatus(DEBUG_USARTx, USART_FLAG_RXNE) == RESET);return (int)USART_ReceiveData(DEBUG_USARTx);
}
bsp_led.c
#include "./led/bsp_led.h" void LED_GPIO_Config(void)
{
/*定义一个GPIO_InitTypeDef类型的结构体*/
GPIO_InitTypeDef GPIO_InitStructure;/*开启LED相关的GPIO外设时钟*/
RCC_APB2PeriphClockCmd( LED1_GPIO_CLK | LED2_GPIO_CLK | LED3_GPIO_CLK, ENABLE);
/*选择要控制的GPIO引脚*/
GPIO_InitStructure.GPIO_Pin = LED1_GPIO_PIN;/*设置引脚模式为通用推挽输出*/
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; /*设置引脚速率为50MHz */
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; /*调用库函数,初始化GPIO*/
GPIO_Init(LED1_GPIO_PORT, &GPIO_InitStructure);/*选择要控制的GPIO引脚*/
GPIO_InitStructure.GPIO_Pin = LED2_GPIO_PIN;/*调用库函数,初始化GPIO*/
GPIO_Init(LED2_GPIO_PORT, &GPIO_InitStructure);/*选择要控制的GPIO引脚*/
GPIO_InitStructure.GPIO_Pin = LED3_GPIO_PIN;/*调用库函数,初始化GPIOF*/
GPIO_Init(LED3_GPIO_PORT, &GPIO_InitStructure);/* 关闭所有led灯*/
GPIO_SetBits(LED1_GPIO_PORT, LED1_GPIO_PIN);/* 关闭所有led灯*/
GPIO_SetBits(LED2_GPIO_PORT, LED2_GPIO_PIN); /* 关闭所有led灯*/
GPIO_SetBits(LED3_GPIO_PORT, LED3_GPIO_PIN);
}void assert_failed(uint8_t* file, uint32_t line)
{
// 断言错误时执行的代码
LED1_ON;
}
bsp_exit.c
#include "bsp_exti.h"static void NVIC_Configuration(void)
{NVIC_InitTypeDef NVIC_InitStructure;/* 配置NVIC为优先级组1 */NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1);/* 配置中断源:按键1 */NVIC_InitStructure.NVIC_IRQChannel = KEY1_INT_EXTI_IRQ;/* 配置抢占优先级 */NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;/* 配置子优先级 */NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;/* 使能中断通道 */NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;NVIC_Init(&NVIC_InitStructure);/* 配置中断源:按键2,其他使用上面相关配置 */ NVIC_InitStructure.NVIC_IRQChannel = KEY2_INT_EXTI_IRQ;NVIC_Init(&NVIC_InitStructure);
}/*** @brief 配置 IO为EXTI中断口,并设置中断优先级* @param 无* @retval 无*/
void EXTI_Key_Config(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
EXTI_InitTypeDef EXTI_InitStructure;/*开启按键GPIO口的时钟*/
RCC_APB2PeriphClockCmd(KEY1_INT_GPIO_CLK,ENABLE);/* 配置 NVIC 中断*/
NVIC_Configuration();/*--------------------------KEY1配置-----------------------------*/
/* 选择按键用到的GPIO */GPIO_InitStructure.GPIO_Pin = KEY1_INT_GPIO_PIN;/* 配置为浮空输入 */GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;GPIO_Init(KEY1_INT_GPIO_PORT, &GPIO_InitStructure);/* 选择EXTI的信号源 */GPIO_EXTILineConfig(KEY1_INT_EXTI_PORTSOURCE, KEY1_INT_EXTI_PINSOURCE); EXTI_InitStructure.EXTI_Line = KEY1_INT_EXTI_LINE;/* EXTI为中断模式 */EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;
/* 上升沿中断 */EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising;/* 使能中断 */EXTI_InitStructure.EXTI_LineCmd = ENABLE;EXTI_Init(&EXTI_InitStructure);/*--------------------------KEY2配置-----------------------------*/
/* 选择按键用到的GPIO */GPIO_InitStructure.GPIO_Pin = KEY2_INT_GPIO_PIN;/* 配置为浮空输入 */GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;GPIO_Init(KEY2_INT_GPIO_PORT, &GPIO_InitStructure);/* 选择EXTI的信号源 */GPIO_EXTILineConfig(KEY2_INT_EXTI_PORTSOURCE, KEY2_INT_EXTI_PINSOURCE); EXTI_InitStructure.EXTI_Line = KEY2_INT_EXTI_LINE;/* EXTI为中断模式 */EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;
/* 下降沿中断 */EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling;/* 使能中断 */EXTI_InitStructure.EXTI_LineCmd = ENABLE;EXTI_Init(&EXTI_InitStructure);
}
bsp_dht11.c
#include "./dht11/bsp_dht11.h"
#include "./dwt_delay/core_delay.h" /* 可以在下面的宏定义中把后面的延时函数替换换SysTick的延时函数,就是想用那个就换成那个的 */#define DHT11_DELAY_US(us) CPU_TS_Tmr_Delay_US(us)
#define DHT11_DELAY_MS(ms) CPU_TS_Tmr_Delay_MS(ms)static void DHT11_GPIO_Config ( void );
static void DHT11_Mode_IPU ( void );
static void DHT11_Mode_Out_PP ( void );
static uint8_t DHT11_ReadByte ( void );/*** @brief DHT11 初始化函数* @param 无* @retval 无*/
void DHT11_Init ( void )
{DHT11_GPIO_Config ();DHT11_Dout_1; // 拉高GPIOB10
}/** 函数名:DHT11_GPIO_Config* 描述 :配置DHT11用到的I/O口* 输入 :无* 输出 :无*/
static void DHT11_GPIO_Config ( void )
{ /*定义一个GPIO_InitTypeDef类型的结构体*/GPIO_InitTypeDef GPIO_InitStructure; /*开启DHT11_Dout_GPIO_PORT的外设时钟*/DHT11_Dout_SCK_APBxClock_FUN ( DHT11_Dout_GPIO_CLK, ENABLE ); /*选择要控制的DHT11_Dout_GPIO_PORT引脚*/ GPIO_InitStructure.GPIO_Pin = DHT11_Dout_GPIO_PIN; /*设置引脚模式为通用推挽输出*/GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; /*设置引脚速率为50MHz */ GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; /*调用库函数,初始化DHT11_Dout_GPIO_PORT*/GPIO_Init ( DHT11_Dout_GPIO_PORT, &GPIO_InitStructure ); }/** 函数名:DHT11_Mode_IPU* 描述 :使DHT11-DATA引脚变为上拉输入模式* 输入 :无* 输出 :无*/
static void DHT11_Mode_IPU(void)
{GPIO_InitTypeDef GPIO_InitStructure;/*选择要控制的DHT11_Dout_GPIO_PORT引脚*/ GPIO_InitStructure.GPIO_Pin = DHT11_Dout_GPIO_PIN;/*设置引脚模式为浮空输入模式*/ GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU ; /*调用库函数,初始化DHT11_Dout_GPIO_PORT*/GPIO_Init(DHT11_Dout_GPIO_PORT, &GPIO_InitStructure); }/** 函数名:DHT11_Mode_Out_PP* 描述 :使DHT11-DATA引脚变为推挽输出模式* 输入 :无* 输出 :无*/
static void DHT11_Mode_Out_PP(void)
{GPIO_InitTypeDef GPIO_InitStructure;/*选择要控制的DHT11_Dout_GPIO_PORT引脚*/ GPIO_InitStructure.GPIO_Pin = DHT11_Dout_GPIO_PIN; /*设置引脚模式为通用推挽输出*/GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; /*设置引脚速率为50MHz */ GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;/*调用库函数,初始化DHT11_Dout_GPIO_PORT*/GPIO_Init(DHT11_Dout_GPIO_PORT, &GPIO_InitStructure); }/* * 从DHT11读取一个字节,MSB先行*/
static uint8_t DHT11_ReadByte ( void )
{uint8_t i, temp=0;for(i=0;i<8;i++) { /*每bit以50us低电平标置开始,轮询直到从机发出 的50us 低电平 结束*/ while(DHT11_Dout_IN()==Bit_RESET);/*DHT11 以26~28us的高电平表示“0”,以70us高电平表示“1”,*通过检测 x us后的电平即可区别这两个状 ,x 即下面的延时 */DHT11_DELAY_US(40); //延时x us 这个延时需要大于数据0持续的时间即可 if(DHT11_Dout_IN()==Bit_SET)/* x us后仍为高电平表示数据“1” */{/* 等待数据1的高电平结束 */while(DHT11_Dout_IN()==Bit_SET);temp|=(uint8_t)(0x01<<(7-i)); //把第7-i位置1,MSB先行 }else // x us后为低电平表示数据“0”{ temp&=(uint8_t)~(0x01<<(7-i)); //把第7-i位置0,MSB先行}}return temp;}/** 一次完整的数据传输为40bit,高位先出* 8bit 湿度整数 + 8bit 湿度小数 + 8bit 温度整数 + 8bit 温度小数 + 8bit 校验和 */
uint8_t DHT11_Read_TempAndHumidity(DHT11_Data_TypeDef *DHT11_Data)
{ /*输出模式*/DHT11_Mode_Out_PP();/*主机拉低*/DHT11_Dout_0;/*延时18ms*/DHT11_DELAY_MS(18);/*总线拉高 主机延时30us*/DHT11_Dout_1; DHT11_DELAY_US(30); //延时30us/*主机设为输入 判断从机响应信号*/ DHT11_Mode_IPU();/*判断从机是否有低电平响应信号 如不响应则跳出,响应则向下运行*/ if(DHT11_Dout_IN()==Bit_RESET) {/*轮询直到从机发出 的80us 低电平 响应信号结束*/ while(DHT11_Dout_IN()==Bit_RESET);/*轮询直到从机发出的 80us 高电平 标置信号结束*/while(DHT11_Dout_IN()==Bit_SET);/*开始接收数据*/ DHT11_Data->humi_int= DHT11_ReadByte();DHT11_Data->humi_deci= DHT11_ReadByte();DHT11_Data->temp_int= DHT11_ReadByte();DHT11_Data->temp_deci= DHT11_ReadByte();DHT11_Data->check_sum= DHT11_ReadByte();/*读取结束,引脚改为输出模式*/DHT11_Mode_Out_PP();/*主机拉高*/DHT11_Dout_1;/*检查读取的数据是否正确*/if(DHT11_Data->check_sum == DHT11_Data->humi_int + DHT11_Data->humi_deci + DHT11_Data->temp_int+ DHT11_Data->temp_deci)return SUCCESS;else return ERROR;}elsereturn ERROR;}
bsp_delay.c
/*使用内核寄存器精确延时*/
#include "./dwt_delay/core_delay.h" /*
**********************************************************************
* 时间戳相关寄存器定义
**********************************************************************
*/
/*在Cortex-M里面有一个外设叫DWT(Data Watchpoint and Trace),该外设有一个32位的寄存器叫CYCCNT,它是一个向上的计数器,记录的是内核时钟运行的个数,最长能记录的时间为:60s=2的32次方/72000000(假设内核频率为72M,内核跳一次的时间大概为1/72M=13.8ns)当CYCCNT溢出之后,会清0重新开始向上计数。使能CYCCNT计数的操作步骤:1、先使能DWT外设,这个由另外内核调试寄存器DEMCR的位24控制,写1使能2、使能CYCCNT寄存器之前,先清03、使能CYCCNT寄存器,这个由DWT_CTRL(代码上宏定义为DWT_CR)的位0控制,写1使能*/#if USE_DWT_DELAY#define DWT_CR *(__IO uint32_t *)0xE0001000
#define DWT_CYCCNT *(__IO uint32_t *)0xE0001004
#define DEM_CR *(__IO uint32_t *)0xE000EDFC#define DEM_CR_TRCENA (1 << 24)
#define DWT_CR_CYCCNTENA (1 << 0)/*** @brief 初始化时间戳* @param 无* @retval 无* @note 使用延时函数前,必须调用本函数*/
void CPU_TS_TmrInit(void)
{/* 使能DWT外设 */DEM_CR |= (uint32_t)DEM_CR_TRCENA; /* DWT CYCCNT寄存器计数清0 */DWT_CYCCNT = (uint32_t)0u;/* 使能Cortex-M DWT CYCCNT寄存器 */DWT_CR |= (uint32_t)DWT_CR_CYCCNTENA;
}/*** @brief 读取当前时间戳* @param 无* @retval 当前时间戳,即DWT_CYCCNT寄存器的值*/
uint32_t CPU_TS_TmrRd(void)
{ return ((uint32_t)DWT_CYCCNT);
}///**
// * @brief 读取当前时间戳
// * @param 无
// * @retval 当前时间戳,即DWT_CYCCNT寄存器的值
// * 此处给HAL库替换HAL_GetTick函数,用于os
// */
//uint32_t HAL_GetTick(void)
//{
// return ((uint32_t)DWT_CYCCNT*1000/SysClockFreq);
//}/*** @brief 采用CPU的内部计数实现精确延时,32位计数器* @param us : 延迟长度,单位1 us* @retval 无* @note 使用本函数前必须先调用CPU_TS_TmrInit函数使能计数器,或使能宏CPU_TS_INIT_IN_DELAY_FUNCTION最大延时值为8秒,即8*1000*1000*/
void CPU_TS_Tmr_Delay_US(__IO uint32_t us)
{uint32_t ticks;uint32_t told,tnow,tcnt=0;/* 在函数内部初始化时间戳寄存器, */
#if (CPU_TS_INIT_IN_DELAY_FUNCTION) /* 初始化时间戳并清零 */CPU_TS_TmrInit();
#endifticks = us * (GET_CPU_ClkFreq() / 1000000); /* 需要的节拍数 */ tcnt = 0;told = (uint32_t)CPU_TS_TmrRd(); /* 刚进入时的计数器值 */while(1){tnow = (uint32_t)CPU_TS_TmrRd(); if(tnow != told){ /* 32位计数器是递增计数器 */ if(tnow > told){tcnt += tnow - told; }/* 重新装载 */else {tcnt += UINT32_MAX - told + tnow; } told = tnow;/*时间超过/等于要延迟的时间,则退出 */if(tcnt >= ticks)break;} }
}#endif
介绍
1.项目名称:基于cortex-M3的QTQT串口助手
2.开发语言:C、C++
3.开发平台:Windows10
4.硬件平台:STM32F103(cortex-M3)、DHT11
5.开发工具:QT5.14.2、Keil5
6.关于项目:
项目主要实现了串行通信软件上位机(PC),和下位机(嵌入式系统)的串口通信系统,串口调试软件,向下位机发送控制信号,控制下位机RGB灯 亮起不同颜色 和 熄灭;操作下位机按键1,向上位机发送本组成员的学号和姓名;操作下位机按键2,接收下位机传来的温湿度传感器检测数据。
7.负责模块:
嵌入式系统下位机开发,按键中断发送数据,获取上位机控制信号;
QT上位机开发,上位机接收数据并做出相应的处理,向下位机发送控制信号;
8.扩展:定时向上位机发送本组成员的姓名信息(定时器中断)
硬件
野火F103指南者+DHT11温湿度传感器
QT全部程序构成
QT设计的思路
首先使用UI设计,搭建出串口调试界面,分为左右两侧GroupBox组控件,左侧数据区放置TextBrowser文本阅读器控件、PlainTextEdit纯文本编辑器控件和PushButton按键控件;右侧串口区放置Label标签控件、ComboBox组合选择框控件、CheckBox复选框控件、SpinBox数值微调器控件和PushButton按键控件;放置完成UI和对象与类如图6所示;
图6 放置完成UI和对象与类
其中对窗口名称设置使用this->setWindowTitle(“xxx的串口调试GUI”); 窗口图标先添加QT Resource File类文件,然后添加 .ico 格式图片,设置在zua.pro文件中添加 RC_ICONS =system.ico;
在对PushButton按键控件使用QT集成的转到槽函数,自动生成槽函数框架,再对不同操作进行逻辑编写,进而实现不同按键的功能;
Keil全部程序构成
Keil程序设计的思路
图2 程序设计的思路
按键K1向上位机发送本组成员的学号和姓名功能和按键K2向上位机发送温湿度功能,在stm32f10x_it.c文件,通过中断函数实现,逻辑图如图3所示;
图3 按键逻辑设计
在main.c文件中while(1)主逻辑程序,设计实现上位机发送控制信号,下位机通过switch()函数选择二极管灯发出不同颜色的光,主逻辑程序设计如图4所示;
图4 主逻辑控制RGB彩灯颜色程序
功能
1.
调试界面初始时,使用失能ui->BtnSend->setEnabled(false); 发送按键为灰色
2.
选择端口,设置波特率、数据位、停止位和校验位,打开串口,开发板按下复位键
3.
复位后,RGB灯为红色,向下位机发送控制信号,在发送数据区域发送2,控制下位机二极管灯发出不同颜色的光,发送2如下图10所示,下位机二极管亮起绿灯如下图11所示。
图10
图11 下位机二极管亮起绿灯
图12 发送3串口调试界面
图13 下位机二极管亮起蓝灯
图14 发送4串口调试界面
图15 下位机二极管亮起蓝灯
还有5-紫色,6-青色,7-白色,8-关闭,other-信息
4.
操作下位机按键K1,向上位机发送本组成员的学号和姓名
5.
操作下位机按键K2,向上位机发送温湿度,接收下位机传来的温湿度传感器检测数据
6.
点击关闭串口,会同时执行窗口清除ui->textRecv->clear();
全部功能
BUG解决方法
- 传输学号姓名时,数据部分丢失,解决:在丢失数据部分 加一个杠
-
解决