QT 实现串口上位机与STM32的通信系统

目标:
通过自定义通信协议,达到以串口发送指令,使STM32单片机能够根据指令控制继电器的通断时间,从而方便开关机的实验。

上位机的使用方法:
自定义通信协议( ee 00 12 04 ff)
ee:帧数据的头部
00 12:十六进制数 --》十进制为18 则代表导通时间为18s
04:继电器的断开时间十进制为4 则代表为断开时间为4s
ff:帧的尾部
注意:
在输入数据时,输入ee的头部不能出现空格。
在未输入指令时,系统默认导通60s,断开4s;
在输入指令时导通时间需至少2s,断开时间需至少2s。
注意单片机板卡是否支持USB转TTL电路;
先插入USB再启动上位机软件。





第一部分:完成上位机的编写
#ifndef RELAYSYS_H
#define RELAYSYS_H

#include <QWidget>
#include <QSerialPort> //提供访问串口的功能
#include <QSerialPortInfo> //提供系统中存在的串口信息
#include <qtimer.h>
#include <QDebug>
#include <QStringList>
#include <QMessageBox>
#include <QTextBlock>

namespace Ui {
class RelaySys;
}

class RelaySys : public QWidget
{
    Q_OBJECT

public:
    explicit RelaySys(QWidget *parent = 0);
    ~RelaySys();

    void serialPortInit(void); //串口初始化

    char convertCharToHex(char ch);
    int QString2HexInt(QString str);

private slots:
    void on_pushButton_OpenSerial_clicked();

    void on_pushButton_send_clicked();  //发送按钮槽函数
    void on_serialPortRecv(void); //串口接收槽函数
    void on_timeUpData(void);
    void on_pushButton_clearRecv_clicked();

    void on_pushButton_clearSend_clicked();

private:
    Ui::RelaySys *ui;

    QSerialPort *serial; //创建串口对象
    QTimer *timer = new QTimer(); //创建定时器,用于在规定的时间内对接收到的数据进行处理
    bool flag = true;
    QByteArray buf; //接收串口发送过来的数据
    int sendBuffer[5];
};

#endif // RELAYSYS_H

源文件

#include "relaysys.h"
#include "ui_relaysys.h"

RelaySys::RelaySys(QWidget *parent) :
    QWidget(parent),
    ui(new Ui::RelaySys)
{
    ui->setupUi(this);

    this->setWindowTitle("继电器控制软件");
    serial = new QSerialPort;
    serialPortInit();

    //连接信号槽函数,当下位机发送数据QSerialPortInfo会发送readRead信号
    connect(serial,SIGNAL(readyRead()),this,SLOT(on_serialPortRecv()));

    connect(timer,SIGNAL(timeout()),this,SLOT(on_timeUpData()));
}


RelaySys::~RelaySys()
{
    delete serial;
    delete ui;
}


void RelaySys::serialPortInit()
{
    foreach (const QSerialPortInfo &info,QSerialPortInfo::availablePorts())
    {
        ui->comboBox_port->addItem(info.portName());//添加串口号名字
        serial->setPort(info);

    }
#if 1
    QStringList baudList;
    QStringList checkList;
    QStringList dataBitList;
    QStringList stopBitList;

    baudList << "4800" << "9600" << "14400" << "19200"
             << "38400"<< "56000"<< "57600" << "115200";
    ui->comboBox_baud->addItems(baudList);
    ui->comboBox_baud->setCurrentIndex(7);

    checkList << "No" << "Even" << "Odd" << "Space" << "Mark";
    ui->comboBox_check->addItems(checkList);
    ui->comboBox_check->setCurrentIndex(0);

    dataBitList << "5 bit" << "6 bit" << "7 bit" << "8 bit";
    ui->comboBox_dataBit->addItems(dataBitList);
    ui->comboBox_dataBit->setCurrentIndex(3);

    stopBitList << "1 bit" << "1.5 bit" << "2 bit";
    ui->comboBox_stopBit->addItems(stopBitList);
    ui->comboBox_stopBit->setCurrentIndex(0);

#else
    //增加波特率下拉框选项
    ui->comboBox_baud->addItem("4800");
    ui->comboBox_baud->addItem("9600");
    ui->comboBox_baud->addItem("14400");
    ui->comboBox_baud->addItem("19200");
    ui->comboBox_baud->addItem("38400");
    ui->comboBox_baud->addItem("56000");
    ui->comboBox_baud->addItem("57600");
    ui->comboBox_baud->addItem("115200");

    //默认选择第7个115200这个波特率
    ui->comboBox_baud->setCurrentIndex(7);

    //增加数据位下拉框选项
    ui->comboBox_dataBit->addItem("5");
    ui->comboBox_dataBit->addItem("6");
    ui->comboBox_dataBit->addItem("7");
    ui->comboBox_dataBit->addItem("8");
    ui->comboBox_dataBit->setCurrentIndex(3);


    //增加停止位下拉框选项
    ui->comboBox_stopBit->addItem("1 bit");
    ui->comboBox_stopBit->addItem("1.5 bit");
    ui->comboBox_stopBit->addItem("2 bit");
    ui->comboBox_stopBit->setCurrentIndex(0);

    ui->comboBox_check->addItem("NONE");

    /*
    ui->comboBox_port->clear();
    //获取可连接的所有的串口的名字
    QList<QSerialPortInfo>serialportInfos = QSerialPortInfo::availablePorts();
    qDebug()<< "Total numbers of ports:"<<serialportInfos.count();

    //用于循环访问serialportInfos集合中所需要的信息
    foreach (const QSerialPortInfo info,serialportInfos)
    {
        ui->comboBox_port->addItem(info.portName());//添加串口号名字
    }
    */

 #endif
}

void RelaySys::on_pushButton_OpenSerial_clicked()
{
    qDebug() << ui->pushButton_OpenSerial->text() << endl;

    if(ui->pushButton_OpenSerial->text() == "打开")
    {
        serial->setPortName(ui->comboBox_port->currentText()); //设置串口名

        if(!serial->open(QIODevice::ReadWrite))//用ReadWrite的模式尝试打开串口
        {

             QMessageBox::about(NULL,"提示","串口打开\r\n不存在或被其它程序占用");
             qDebug() << "打开失败!" << endl;
                return;
        }
        else
        {
            qDebug()<<  endl << "打开成功" << endl;
            //设置波特率
            serial->setBaudRate(ui->comboBox_baud->currentText().toInt());

            //设置校验位
            switch(ui->comboBox_check->currentIndex())
            {
                case 0: serial->setParity(QSerialPort::NoParity);break;
                case 1: serial->setParity(QSerialPort::EvenParity);break;
                case 2: serial->setParity(QSerialPort::OddParity);break;
                case 3: serial->setParity(QSerialPort::SpaceParity);break;
                case 4: serial->setParity(QSerialPort::MarkParity);break;
                default: break;
            }

            //设置数据位
            switch(ui->comboBox_dataBit->currentIndex())
            {
                case 0: serial->setDataBits(QSerialPort::Data5);break;
                case 1: serial->setDataBits(QSerialPort::Data6);break;
                case 2: serial->setDataBits(QSerialPort::Data7);break;
                case 3: serial->setDataBits(QSerialPort::Data8);break;
                default: break;
            }
            //设置停止位
            switch(ui->comboBox_stopBit->currentIndex())
            {
                case 0: serial->setStopBits(QSerialPort::OneStop);break;
                case 1: serial->setStopBits(QSerialPort::OneAndHalfStop);break;
                case 2: serial->setStopBits(QSerialPort::TwoStop);break;
                default: break;
            }
            //设置流控
            serial->setFlowControl(QSerialPort::NoFlowControl);

            ui->comboBox_baud->setEnabled(false);
            ui->comboBox_check->setEnabled(false);
            ui->comboBox_dataBit->setEnabled(false);
            ui->comboBox_port->setEnabled(false);
            ui->comboBox_stopBit->setEnabled(false);
            ui->pushButton_OpenSerial->setText("关闭");
        }
     }
    else
    {
        if(serial->isOpen()) //如果串口打开则将其关闭
        {
            serial->clear();
            serial->close();
        }
        ui->pushButton_OpenSerial->setText("打开");
        ui->comboBox_baud->setEnabled(true);
        ui->comboBox_check->setEnabled(true);
        ui->comboBox_dataBit->setEnabled(true);
        ui->comboBox_port->setEnabled(true);
        ui->comboBox_stopBit->setEnabled(true);

        qDebug() << "关闭成功" << endl;
    }

}

void RelaySys::on_pushButton_send_clicked()
{

    int i = 0;
    short value = 0; //记录导通的有效时间
    int SIZE = 0; //接收个数
    char *pBuf  = (char *)&value;
    QString temp;

    //获取textEdit中的内容 toPlainText()获取文本函数
    QString str = ui->textEdit->toPlainText();

    QStringList list = str.split(" ");//以指定的字符“ ”空格分割支付串
    SIZE = sizeof(sendBuffer)/sizeof(sendBuffer[0]);
    //qDebug() << "SIZE = " << SIZE;

    if(list.length() > SIZE && list[SIZE] != NULL)
    {
        QMessageBox::about(NULL, "提示", "请输入有效的指令!");
        return ;
    }

    for(i = 0; i < SIZE; i++)
    {
        temp = list[i];
        if(temp.length() > 2 || temp.length() == 1 )
        {
            QMessageBox::about(NULL, "提示", "请输入有效的指令!");
            return ;
        }
        sendBuffer[i] = QString2HexInt(temp); //转化为十进制
        /*sendBuffer[i] = temp.toInit(&ok,16);*/
         qDebug() << sendBuffer[i] << " ";
    }

    pBuf[0] = sendBuffer[2];
    pBuf[1] = sendBuffer[1];
    //qDebug() << "value == " << value << endl;

    //对帧数据进行校验
    if(list[0] != "ee" || value < 2 || sendBuffer[3] < 2 || list[4] != "ff")
    {
        QMessageBox::about(NULL, "提示", "输入的指令不正确,请重新输入;格式: ee 导通时间>=2s 断开时间>=2s ff");
        return ;
    }
    else
    {
        QByteArray buffer;
        buffer.resize(SIZE);
        for(int i = 0; i < SIZE; i++)
        {
            buffer[i] = sendBuffer[i];
        }
        serial->write(buffer);
    }

}


void RelaySys::on_timeUpData(void)
{

    timer->stop();
    //时间到了处理数据


}

void RelaySys::on_serialPortRecv(void)
{
    timer->start(100); //使用定时器定时接收数据,非阻塞机制,100ms后自动认为接收数据完成
    //buf = serial->readAll();
    buf.append(serial->readAll());
    int i = 0;
    QString RecvBuffer; //存放十六进制数据
    QString st;  //使能够显示中文
    QString str;
    if(!buf.isEmpty()) //若非空则表示有数据接收
    {
        st = QString::fromLocal8Bit(buf); //需能显示中文
        qDebug() << "st = " << st << endl;

        if(ui->checkBox_show16->isChecked())
        {
            str = buf.toHex().data(); //转换成十六进制
            str = str.toUpper(); //转换成大写
            for(i = 0; i < str.length(); i += 2)
            {
               QString temp = str.mid(i, 2);
               RecvBuffer += temp;
               RecvBuffer += " ";
            }
            ui->plainTextEdit->appendPlainText(RecvBuffer);
        }
        else
        {
            ui->plainTextEdit->appendPlainText(st);
        }
    }


    QString line = QString::number(ui->plainTextEdit->document()->lineCount());
    // QMessageBox::information(this,"information",line);
    int line_val = line.toInt();
    qDebug() << "line = " << line_val;
    // QString data = ui->plainTextEdit->document()->findBlockByLineNumber(line_val).text();
    //qDebug() << "data = " << data;

    buf.clear();

}


//将一个字节的字符串转化为整型  十六进制转化为十进制  如“0x1f"--->31
int RelaySys::QString2HexInt(QString str)
{
    QByteArray byte;
    byte = str.toLatin1(); //单字节编码兼容ASCII

    int high = convertCharToHex(byte[0]);
    int low = convertCharToHex(byte[1]);
    return high*16 + low;
}


char RelaySys::convertCharToHex(char ch)
 {
     if((ch >= '0') && (ch <= '9'))
         return ch - 0x30; //0x30 -->48
     else if((ch >= 'A') && (ch <= 'F'))
         return ch - 'A' +10;
     else if((ch >= 'a') && (ch <= 'f'))
         return ch - 'a' + 10;
     else
         return -1;
 }

void RelaySys::on_pushButton_clearRecv_clicked()
{
      ui->plainTextEdit->clear();
}

void RelaySys::on_pushButton_clearSend_clicked()
{
    ui->textEdit->clear();
}

在这里插入图片描述


第二部分:关于keil的工程创建及功能实现

main.c

#include "main.h"
#include "core_cm0.h"
#include "relay.h"
#include "usart.h"
#include "delay.h"
#include "TIM_delay.h"

extern uint8_t gRecvBuff[5];
extern uint8_t gRecvFlag;
extern uint32_t gCount;

void SysTick_Delay_Ms( __IO uint32_t ms)
{
	uint32_t i;	
	SysTick_Config(SystemCoreClock/1000);
	
	for(i=0;i<ms;i++)
	{
		// 当计数器的值减小到0的时候,CRTL寄存器的位16会置1
		// 当置1时,读取该位会清0
		while( !((SysTick->CTRL)&(1<<16)) );
	}
	// 关闭SysTick定时器
	SysTick->CTRL &=~ SysTick_CTRL_ENABLE_Msk;
}

 
 
void SYSTICK_INIT(void)
{
		SysTick_Config(SystemCoreClock / 1000);	//Set SysTick Timer for 1ms interrupts
		//SysTick_Config(SystemCoreClock / 200);	//Set SysTick Timer for 5ms interrupts
		//SysTick_Config(SystemCoreClock / 100);	//Set SysTick Timer for 10ms interrupts
		//SysTick_Config(SystemCoreClock / 10);	//Set SysTick Timer for 100ms interrupts     
}
 

void delay(int num)
{
	unsigned int  i = 0;
	while(num--)
	{
		for(i = 0; i < 1650; i++);
	}

}
/**
  * @brief  Main program
  * @param  None
  * @retval None
  */
int main(void)
{
		uint8_t i = 0;
		uint16_t num = 0;
		uint8_t *pBuf = (uint8_t *)&num;
	
		uint16_t OnTimes = 0;
		uint32_t tempCnt = gCount;
		USART_Config(115200);
		delay_init();
	
		TIMx_Init(999,47);
		relay_init();
	
	
//		printf("format: 0xEE 0xMS1 0xMS0 0xFF \n");
//		delay_ms(10);
//		printf("ms1:导通时间,ms0:断开时间\n");

	
	loop:	
		while (1)
		{
			
					if(gRecvFlag == 0) //未收到指令时,默认是导通60s,断开4s
					{
						relay_on();
						OnTimes++;
						printf("Times = %d\n",OnTimes);
						
						for(i = 0; i < 60; i++)
						{
							if(gCount == 1) //若第一条指令到来,退出默认状态 关闭导通
							{
								//printf("gCount = %d\n",gCount);
								tempCnt = gCount;
								relay_off(); 
								OnTimes = 0;
								delay_ms(1000); //不加延时无法看出新指令执行效果
								break;
							}
							delay_ms(1000);
						}
						
						relay_off();
						for(i = 0; i < 4; i++)
						{
							if(gCount == 1) //若第一条指令到来则,退出默认状态
							{
								tempCnt = gCount;
								relay_off();
								OnTimes = 0;
								delay_ms(1000);
								break;
							}
							delay_ms(1000);
						}
						
					}
					else
					{
						while(!gRecvFlag); //收到指令
//					for(i = 0; i < 4; i++)
//					{
//						printf("gRecvBuff[%d] =  %x\n",i,gRecvBuff[i]);
//					}
						relay_on();
						OnTimes++;
						printf("Times = %d\n",OnTimes);
						
						pBuf[0] = gRecvBuff[2];
						pBuf[1] = gRecvBuff[1];
					
						//printf("0x%02x \n",num);
						for( i = 0; i < num; /* i < gRecvBuff[1]*10; */ i++)
						{
							
							if(tempCnt + 1 ==  gCount) 
							{
								//printf("tempCnt = %d\n",tempCnt);
								tempCnt = gCount;  //tempCnt++;
								relay_off();
								OnTimes = 0;
								delay_ms(2000);
								goto loop;
							}
							delay_ms(1000);
						
						}		
						relay_off();
						for( i = 0; i < gRecvBuff[3]; i++)
						{
							if(tempCnt + 1 ==  gCount)
							{
								tempCnt = gCount; //tempCnt++;
								relay_off();
								OnTimes = 0;
								delay_ms(2000);
								goto loop;
							}
			
							delay_ms(1000);
						}	
					}						
		}
}







#ifdef  USE_FULL_ASSERT

void assert_failed(uint8_t* file, uint32_t line)
{ 

  while (1)
  {
  }
}
#endif
 




relay.h

#ifndef RELAY_H_
#define RELAY_H_
#include "stm32f0xx.h"

void relay_init(void);
void relay_on(void);
void relay_off(void);


#endif /* RELAY_H_*/

relay.c

#include "relay.h"

void relay_init(void)
{ 
	 GPIO_InitTypeDef  GPIO_InitStructure;
	 RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOB, ENABLE);	//时钟配置
		
	 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2 | GPIO_Pin_11;				
	 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT; 	
	 GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; //推挽模式输出
	 //GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL; //无上下拉(浮空)
	 GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP; //上拉模式 
	 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_10MHz;	
	
	 GPIO_SetBits(GPIOB, GPIO_Pin_11);
	 GPIO_Init(GPIOB, &GPIO_InitStructure);
}

void relay_on(void)
{
	GPIO_SetBits(GPIOB,GPIO_Pin_2);
	//GPIOB->BSRR = GPIO_Pin_2;
	GPIO_ResetBits(GPIOB, GPIO_Pin_11);

}

void relay_off(void)
{
	GPIO_ResetBits(GPIOB,GPIO_Pin_2);
	//GPIOB->BRR = GPIO_Pin_2;
	GPIO_SetBits(GPIOB, GPIO_Pin_11);

}



delay.h

#ifndef DELAY_H_
#define DELAY_H_
#include "stm32f0xx.h"
#include "core_cm0.h"

void delay_init(void);

void delay_us(uint32_t nus);

void delay_ms(uint32_t nms);


void Delay_ms(uint32_t nms);
void Delay_us(uint32_t nus);


#endif /*DELAY_H_*/

delay.c

#include "delay.h"

static uint8_t  fac_us=0;							//us延时倍乘数			   
static uint16_t  fac_ms=0;							//ms延时倍乘数,在ucos下,代表每个节拍的ms数


//查询方式
void delay_init(void)
{
	//SysTick_CLKSourceConfig(SysTick_CLKSource_HCLK_Div8);	//选择外部时钟  HCLK/8
	SysTick->CTRL = 0xFFFFFFFB;
	fac_us = SystemCoreClock / 48000000;				 
	fac_ms = (uint16_t)fac_us * 1000;				  

}


void delay_us(uint32_t nus)
{		
	unsigned int temp;	    	 
	SysTick->LOAD = nus * fac_us; 					//时间加载	  		 
	SysTick->VAL = 0x00;        					//清空计数器
	SysTick->CTRL|=SysTick_CTRL_ENABLE_Msk ;	//打开定时器,开始倒数	  
	do
	{
		temp = SysTick->CTRL;  //读取当前倒计数值
	}while((temp&0x01) && !(temp&(1<<16)));		//等待时间到达   
	SysTick->CTRL &= ~SysTick_CTRL_ENABLE_Msk;	//关闭定时器
	SysTick->VAL = 0X00;      					 //清空计数器	 
}

#if 1
void delay_ms(uint32_t nms)
{	 		  	  
	unsigned int temp;		   
	SysTick->LOAD= (uint32_t)nms * fac_ms;				//时间加载(SysTick->LOAD为24bit)
	SysTick->VAL = 0x00;							//清空计数器
	SysTick->CTRL |= SysTick_CTRL_ENABLE_Msk ;	//开始倒数  
	do
	{
		temp = SysTick->CTRL;
	}while((temp&0x01)&&!(temp&(1<<16)));		//等待时间到达   
	SysTick->CTRL &= ~SysTick_CTRL_ENABLE_Msk;	//关闭定时器
	SysTick->VAL = 0X00;       					//清空计数器	  	    
} 

#else
void delay_ms(uint32_t nms)
{	
	uint32_t i = 0;
	SysTick_Config(48000);
	for(i = 0; i < nms; i++)
	{
		while(!((SysTick->CTRL) & ( 1<<16)));
		
	}
	SysTick->CTRL &=~ SysTick_CTRL_ENABLE_Msk;
}

#endif

/******************************************/
uint32_t TimingDelay = 0;
void Delay_us(uint32_t nus)
{
	SysTick_Config(SystemCoreClock/100000); //10us中断一次
	TimingDelay = nus;
	while(TimingDelay != 0);
}

void Delay_ms(uint32_t nms)
{
	SysTick_Config(SystemCoreClock/1000); //1ms中断一次
	TimingDelay = nms;
	while(TimingDelay != 0);
}

usart.h
#ifndef USART_H_
#define USART_H_
#include "stm32f0xx.h"
#include <stdio.h>


void USART_Config(uint32_t BaudRate);

void USART1_Send_Char(unsigned char c);
void USART1_Send_String(char *s );
void Usart_SendByte( USART_TypeDef * pUSARTx, uint8_t ch);
void Usart_SendArray( USART_TypeDef * pUSARTx, uint8_t *array, uint16_t num);
void Usart_SendString( USART_TypeDef * pUSARTx, char *str);
void Usart_SendHalfWord( USART_TypeDef * pUSARTx, uint16_t ch);



#endif /*USART_H_*/

usart.c

#include "usart.h"
#include "relay.h"
#include "delay.h"

uint8_t gRecvBuff[5];
uint8_t gRecvFlag = 0;

void USART_Config(uint32_t BaudRate)
{ 
    GPIO_InitTypeDef GPIO_InitStructure;  
		USART_InitTypeDef USART_InitStructure;
		NVIC_InitTypeDef 	NVIC_InitStructure;
	
		NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;
		NVIC_InitStructure.NVIC_IRQChannelPriority = 0;
		NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
		NVIC_Init(&NVIC_InitStructure);
	
	  //配置时钟
		RCC_AHBPeriphClockCmd( RCC_AHBPeriph_GPIOA , ENABLE);
		RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE );	
		
	
		GPIO_PinAFConfig(GPIOA, GPIO_PinSource9, GPIO_AF_1); //Tx
		GPIO_PinAFConfig(GPIOA, GPIO_PinSource10, GPIO_AF_1); //Rx   											 
		GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9|GPIO_Pin_10;                 
		GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF; 
		GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
		GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;
		GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
		GPIO_Init(GPIOA, &GPIO_InitStructure);   

		USART_InitStructure.USART_BaudRate = 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(USART1, &USART_InitStructure);
		
	
		USART_ITConfig(USART1,USART_IT_RXNE,ENABLE); //接收一个字节中断
		//USART_ITConfig(USART1,USART_IT_IDLE,ENABLE); //接收一帧数据中断
		USART_Cmd(USART1, ENABLE);
  
}







uint8_t h = 0;
uint32_t gCount = 0;
void USART1_IRQHandler(void)
{
	uint16_t temp = 0;
	USART_ClearITPendingBit(USART1,USART_IT_RXNE);
	
	if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET)//接收数据寄存器不为空
	{
	
		temp = USART_ReceiveData(USART1);
		if((temp == 0xee) && (h == 0))
		{
			h = 1;
			gRecvBuff[0] = temp;
		}
		else if( h == 1)
		{
			h = 2;
			gRecvBuff[1] = temp;
		}
		else if(h == 2)
		{
			h = 3;
			gRecvBuff[2] = temp;
		}
		else if(h == 3)
		{
			h = 4;
			gRecvBuff[3] = temp;
		}
		else if(h == 4)
		{
			gRecvFlag = 1;
			h = 0;
			gRecvBuff[4] = temp;
			gCount++; //记录有效指令条数
			//printf("第%d条指令\n",gCount);
		}
		else
		{
			h = 0;
		}
		
		while(USART_GetFlagStatus(USART1,USART_FLAG_TC) == RESET);
	
	}
}




  // 重定义  
//int fputc(int ch, FILE *f)
//{
//	while (!(USART1->ISR & USART_FLAG_TXE));
//	USART1->TDR = ch;
//  
//  return (ch);
//}



///重定向c库函数printf到串口,重定向后可使用printf函数
int fputc(int ch, FILE *f)
{
		/* 发送一个字节数据到串口 */
		USART_SendData(USART1, (uint8_t) ch);
		
		/* 等待发送完毕 */
		while (USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET);		
	
		return (ch);
}

int fgetc(FILE *f)
{
		/* 等待串口输入数据 */
		while (USART_GetFlagStatus(USART1, USART_FLAG_RXNE) == RESET);

		return (int)USART_ReceiveData(USART1);
}


void USART1_Send_Char(unsigned char c)
{
	while(!((USART1->ISR)&USART_FLAG_TXE));
	USART1->TDR=c;	
}

void USART1_Send_String(char *s )
{
	while (*s)
	USART1_Send_Char(*s++);
}



/*****************  发送一个字节 **********************/
void Usart_SendByte( USART_TypeDef * pUSARTx, uint8_t ch)
{
	/* 发送一个字节数据到USART */
	USART_SendData(pUSARTx,ch);
		
	/* 等待发送数据寄存器为空 */
	while (USART_GetFlagStatus(pUSARTx, USART_FLAG_TXE) == RESET);	
}

/****************** 发送8位的数组 ************************/
void Usart_SendArray( USART_TypeDef * pUSARTx, uint8_t *array, uint16_t num)
{
  uint8_t i = 0;
	
	for(i=0; i< num; i++)
  {
	    /* 发送一个字节数据到USART */
	    Usart_SendByte(pUSARTx,array[i]);	
  
  }
	/* 等待发送完成 */
	while(USART_GetFlagStatus(pUSARTx,USART_FLAG_TC)==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);	
}

总结:
大体的功能基本具备,能够实现目标,但还存在一些问题,编写的代码并未完全利用,对于QT中的串口、定时器、各种进制之间的转换还需注意,串口发送及接收可以更好的处理;基于单片机的系统定时器(滴答定时器),硬件定时,通过串口传输数据等知识点需学习掌握。

遇到的问题:

  1. 能控制继电器但不能够正常工作,查看开发板的原理图时需注意继电器的工作电压,以及连接STM32核心板的引脚,便于控制继电器的工作;

  2. 未能够实现精确延时,需注意每个芯片的晶振频率;

  3. 需对传输的数据进行校验,在新指令到来时的处理需考虑清楚,容易出错;

  4. 对QT串口发送到单片机的数据是何种类型需了解;

  5. 获取程序异常状态信息,通过看门狗实现;

  6. 单片机能发送数据,上位机未能接收。

  • 13
    点赞
  • 179
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
好的,以下是一个简单的示例代码,展示如何使用PyQt5实现蓝牙上位机STM32通信。该示例使用HC-05蓝牙模块和STM32F103开发板进行通信,使用了PyQt5中的QBluetooth和QSerialPort模块。 ```python import sys from PyQt5.QtWidgets import QApplication, QMainWindow, QPushButton, QTextEdit from PyQt5.QtCore import QIODevice, QByteArray, pyqtSignal, pyqtSlot from PyQt5.QtSerialPort import QSerialPort, QSerialPortInfo from PyQt5.QtBluetooth import QBluetoothDeviceDiscoveryAgent, QBluetoothSocket class MainWindow(QMainWindow): def __init__(self): super().__init__() # 初始化串口和蓝牙设备 self.serial_port = QSerialPort() self.bluetooth_socket = QBluetoothSocket(QBluetoothServiceInfo.RfcommProtocol) # 初始化界面 self.init_ui() # 初始化蓝牙设备发现器 self.device_discovery_agent = QBluetoothDeviceDiscoveryAgent() self.device_discovery_agent.deviceDiscovered.connect(self.device_discovered) def init_ui(self): # 设置主窗口标题和大小 self.setWindowTitle('Bluetooth Upper Computer') self.setGeometry(200, 200, 500, 500) # 设置搜索设备按钮 self.search_btn = QPushButton('Search Devices', self) self.search_btn.setGeometry(50, 50, 100, 30) self.search_btn.clicked.connect(self.search_devices) # 设置连接设备按钮 self.connect_btn = QPushButton('Connect Device', self) self.connect_btn.setGeometry(200, 50, 100, 30) self.connect_btn.setEnabled(False) self.connect_btn.clicked.connect(self.connect_device) # 设置发送数据按钮 self.send_btn = QPushButton('Send Data', self) self.send_btn.setGeometry(350, 50, 100, 30) self.send_btn.setEnabled(False) self.send_btn.clicked.connect(self.send_data) # 设置文本编辑框 self.text_edit = QTextEdit(self) self.text_edit.setGeometry(50, 100, 400, 300) def search_devices(self): # 开始搜索蓝牙设备 self.device_discovery_agent.start() def device_discovered(self, device_info): # 当发现蓝牙设备时,将其名称和地址显示在界面上 device_name = device_info.name() device_address = device_info.address().toString() self.text_edit.append('Device Found: ' + device_name + ' (' + device_address + ')') def connect_device(self): # 连接所选的蓝牙设备,并打开串口 device_address = self.selected_device.address().toString() self.bluetooth_socket.connectToService(device_address, QBluetoothUuid.SerialPort) self.serial_port.setPortName('COM3') self.serial_port.setBaudRate(QSerialPort.Baud9600) self.serial_port.setDataBits(QSerialPort.Data8) self.serial_port.setParity(QSerialPort.NoParity) self.serial_port.setStopBits(QSerialPort.OneStop) self.serial_port.setFlowControl(QSerialPort.NoFlowControl) self.serial_port.open(QIODevice.ReadWrite) # 显示连接状态,并启用发送数据按钮 self.text_edit.append('Device Connected: ' + self.selected_device.name()) self.send_btn.setEnabled(True) def send_data(self): # 从文本编辑框中获取发送的数据,并发送到串口 data = self.text_edit.toPlainText().encode() self.serial_port.write(data) def closeEvent(self, event): # 关闭窗口时,关闭串口和蓝牙连接 self.serial_port.close() self.bluetooth_socket.close() # 重载QIODevice的readyRead信号,当串口有数据可读时,将其显示在界面上 @pyqtSlot() def ready_read(self): data = self.serial_port.readAll() self.text_edit.append('Received: ' + str(data, 'utf-8')) if __name__ == '__main__': app = QApplication(sys.argv) window = MainWindow() window.show() sys.exit(app.exec_()) ``` 以上代码仅为示例,您需要根据实际情况进行修改和完善。同时,您需要安装PyQt5、PyQt5.QtSerialPort和PyQt5.QtBluetooth模块才能正常运行该程序。希望对您有所帮助!

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值