参考:【QT】串口助手(优化升级)
其他功能展示在文章 最后
只添加了 QT += core gui serialport
RC_ICONS =system.ico
QT += core gui serialport
greaterThan(QT_MAJOR_VERSION, 4): QT += widgets
CONFIG += c++11
DEFINES += QT_DEPRECATED_WARNINGS
SOURCES += \
main.cpp \
serial.cpp
HEADERS += \
serial.h
FORMS += \
serial.ui
RC_ICONS =system.ico
qnx: target.path = /tmp/$${TARGET}/bin
else: unix:!android: target.path = /opt/$${TARGET}/bin
!isEmpty(target.path): INSTALLS += target
RESOURCES += \
zua.qrc
#ifndef SERIAL_H
#define SERIAL_H
#include
/*---port串口--*/
#include
#include
/*---QString--*/
#include
/*---QTimer定时器--*/
#include
/*-----QToolBar工具栏------*/
#include
/*---QDebug打印--*/
#include
QT_BEGIN_NAMESPACE
namespace Ui { class Serial; }
QT_END_NAMESPACE
class Serial : public QMainWindow
{
Q_OBJECT
public:
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
没有改变
#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);//XI
mIsOpen = 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;
else
hexdata = 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(); //停止定时器
}
}
在Win10下编程,有可能会显示中文乱码
不同编程环境使用不同
1. QString::fromLocal8Bit("");//处理汉语显示乱码,函数返回的是String类
2.QString::fromUtf8("")
还有更多,目前我只用到这两个
槽函数slot 区分
#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");
}
注意文件中 有可能会 注释掉了一些头文件,把注释去掉就好啦
在最后插入以下
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);
}
}
添加了头文件,其他保留
#include "stm32f10x.h"
#include "./led/bsp_led.h"
#include "./usart/bsp_usart.h"
#include "./Key/bsp_exti.h"
#include "./dht11/bsp_dht11.h"
#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);
}
#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;
}
#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);
}
#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;
}
else
return ERROR;
}
/*使用内核寄存器精确延时*/
#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寄存器之前,先清0
3、使能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();
#endif
ticks = 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的QT串口助手
2.开发语言:C、C++
3.开发平台:Windows10
4.硬件平台:STM32F103(cortex-M3)、DHT11
5.开发工具:QT5.14.2、Keil5
6.关于项目:
项目主要实现了串行通信软件上位机(PC),和下位机(嵌入式系统)的串口通信系统,串口调试软件,向下位机发送控制信号,控制下位机RGB灯 亮起不同颜色 和 熄灭;操作下位机按键1,向上位机发送本组成员的学号和姓名;操作下位机按键2,接收下位机传来的温湿度传感器检测数据。
7.负责模块:
嵌入式系统下位机开发,按键中断发送数据,获取上位机控制信号;
QT上位机开发,上位机接收数据并做出相应的处理,向下位机发送控制信号;
8.扩展:定时向上位机发送本组成员的姓名信息(定时器中断)
首先使用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集成的转到槽函数,自动生成槽函数框架,再对不同操作进行逻辑编写,进而实现不同按键的功能;
图2 程序设计的思路
按键K1向上位机发送本组成员的学号和姓名功能和按键K2向上位机发送温湿度功能,在stm32f10x_it.c文件,通过中断函数实现,逻辑图如图3所示;
图3 按键逻辑设计
在main.c文件中while(1)主逻辑程序,设计实现上位机发送控制信号,下位机通过switch()函数选择二极管灯发出不同颜色的光,主逻辑程序设计如图4所示;
图4 主逻辑控制RGB彩灯颜色程序
调试界面初始时,使用失能ui->BtnSend->setEnabled(false); 发送按键为灰色
选择端口,设置波特率、数据位、停止位和校验位,打开串口,开发板按下复位键
复位后,RGB灯为红色,向下位机发送控制信号,在发送数据区域发送2,控制下位机二极管灯发出不同颜色的光,发送2如下图10所示,下位机二极管亮起绿灯如下图11所示。
图10
图11 下位机二极管亮起绿灯
图12 发送3串口调试界面
图13 下位机二极管亮起蓝灯
图14 发送4串口调试界面
图15 下位机二极管亮起蓝灯
还有5-紫色,6-青色,7-白色,8-关闭,other-信息
操作下位机按键K1,向上位机发送本组成员的学号和姓名
操作下位机按键K2,向上位机发送温湿度,接收下位机传来的温湿度传感器检测数据
点击关闭串口,会同时执行窗口清除ui->textRecv->clear();
QT编写时使用QString::fromLocal8Bit(recvData);
处理汉语显示乱码
传输学号姓名时,数据部分丢失,解决:在丢失数据部分 加一个杠 -
解决
- 百度云:
链接:https://pan.baidu.com/s/1C6IcP2pN99cxcWNOrNdxJA?pwd=ifhf
提取码:ifhf