QT串口助手-ZUA课设

成品展示

参考:【QT】串口助手(优化升级)
在这里插入图片描述

其他功能展示在文章 最后

QT全部程序构成

在这里插入图片描述

zua.pro

只添加了 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

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_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

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);//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 区分
在这里插入图片描述在这里插入图片描述

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;
	}
	
	else
		return 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寄存器之前,先清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.扩展:定时向上位机发送本组成员的姓名信息(定时器中断)

硬件

野火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图10
图11 下位机二极管亮起绿灯
在这里插入图片描述
图12 发送3串口调试界面
在这里插入图片描述
图13 下位机二极管亮起蓝灯
在这里插入图片描述
图14 发送4串口调试界面
在这里插入图片描述
图15 下位机二极管亮起蓝灯
在这里插入图片描述

还有5-紫色,6-青色,7-白色,8-关闭,other-信息

4.

操作下位机按键K1,向上位机发送本组成员的学号和姓名
在这里插入图片描述

5.

操作下位机按键K2,向上位机发送温湿度,接收下位机传来的温湿度传感器检测数据
在这里插入图片描述

6.

点击关闭串口,会同时执行窗口清除ui->textRecv->clear();
在这里插入图片描述

全部功能

在这里插入图片描述
在这里插入图片描述

BUG解决方法

  • QT编写时使用QString::fromLocal8Bit(recvData);处理汉语显示乱码
    在这里插入图片描述

  • 传输学号姓名时,数据部分丢失,解决:在丢失数据部分 加一个杠 -
    在这里插入图片描述
    解决
    在这里插入图片描述

源码

  1. 有道云笔记: QT串口助手-ZUA课设
  1. 百度云:
    链接:https://pan.baidu.com/s/1C6IcP2pN99cxcWNOrNdxJA?pwd=ifhf
    提取码:ifhf
  1. Git:03QT串口助手-ZUA课设

猜你喜欢

转载自blog.csdn.net/qq_47355554/article/details/126898479