串口寄存器的配置
这个寄存器的配置值得注意的地方:
端口的选择和时钟使能,时钟使能需要同时打开GPIOA和串口自己的时钟,原因应该是串口是使用的复用功能,所以在串口的Tx端应该GPIO端口模式应该设为复用推挽输出。
void USART1_Init(u32 bound)
{
//GPIO端口设置
GPIO_InitTypeDef GPIO_InitStructure;
USART_InitTypeDef USART_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_USART1, ENABLE); //使能USART1,GPIOA时钟
USART_DeInit(USART1); //复位串口1
//USART1_TX PA.9
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9; //PA.9
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //复用推挽输出
GPIO_Init(GPIOA, &GPIO_InitStructure);
//USART1_RX PA.10
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;//浮空输入
GPIO_Init(GPIOA, &GPIO_InitStructure);
//Usart1 NVIC 配置
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=1 ;//抢占优先级1
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1; //子优先级1
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道使能
NVIC_Init(&NVIC_InitStructure); //根据指定的参数初始化VIC寄存器
//USART 初始化设置
USART_InitStructure.USART_BaudRate = bound;//一般设置为9600;
USART_InitStructure.USART_WordLength = USART_WordLength_8b;//字长为8位数据格式
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);//开启ENABLE/关闭DISABLE中断
USART_Cmd(USART1, ENABLE); //使能串口
}
串口在发送和接收分别使用库函数:
SART_SendData(USART1, data);
temp = USART_ReceiveData(USART1);
当然了,每次发送或接收时,应该判断一下发送的状态,如果标志位为0表示还没有发送完成,则让程序等待。
extern uint8_t temp;
//串口1中断服务程序(固定的函数名不能修改)
int USART1_IRQHandler(void)//串口1中断服务程序(固定的函数名不能修改)
{
while(USART_GetFlagStatus(USART1, USART_FLAG_RXNE) == RESET);//判断是否读取完成
temp = USART_ReceiveData(USART1);
// USART_SendData(USART1,temp);
printf("%c",temp);
while(USART_GetFlagStatus(pUSARTx, USART_FLAG_TXE) == RESET);//状态为0,表示没有发送完成。
return temp;
}
总而言之判断状态有点懵。
//发送一个字节
void uart_sendbyte(USART_TypeDef* pUSARTx, uint8_t data)
{
USART_SendData(pUSARTx, data);
while(USART_GetFlagStatus(pUSARTx, USART_FLAG_TXE) == RESET);//状态为0,表示没有发送完成。
}
//发送两个字节
void uart_sendhalfword(USART_TypeDef* pUSARTx, uint16_t data)
{
uint8_t temp_h, temp_l;
//把data的高位取出存到temp_h里面
temp_h = (data & 0xff00)>>8;
//把data的第八位取出存到temp_l里面
temp_l = (data & 0xff);
//先发高位再发低位
USART_SendData(pUSARTx, temp_h);
while(USART_GetFlagStatus(pUSARTx, USART_FLAG_TXE) == RESET);//状态为0,表示没有发送完成。
USART_SendData(pUSARTx, temp_l);
while(USART_GetFlagStatus(pUSARTx, USART_FLAG_TXE) == RESET);//状态为0,表示没有发送完成。
}
//发送数组
void uart_sendarray(USART_TypeDef* pUSARTx, uint8_t* array, uint8_t num)
{
uint8_t i=0;
for(i = 0; i<num; i++)
{
USART_SendData(pUSARTx, array[i]);
while(USART_GetFlagStatus(pUSARTx, USART_FLAG_TXE) == RESET);
}
while(USART_GetFlagStatus(pUSARTx, USART_FLAG_TC) == RESET);//发送一连串的数据需要用USART_FLAG_TC
}
//发送字符串
void uart_sendstr(USART_TypeDef* pUSARTx, uint8_t* str)
{
do
{
uart_sendbyte(pUSARTx, *(str++));
while(USART_GetFlagStatus(pUSARTx, USART_FLAG_TXE) == RESET);
}while(*str != '\0');
while(USART_GetFlagStatus(pUSARTx, USART_FLAG_TC) == RESET);
}
int fputc(int ch, FILE *f)
{
uart_sendbyte(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);
}
这个还需要注意的是用USART_SendData(pUSARTx, data)发送数据时,用16进制显示是正确的,但用十进制却是乱码,感觉有点离谱,所以用printf(“%d”, data)好使一些。
这里没有用中断,虽然中断设置打开了。这里看一下main函数
#include "stm32f10x.h"
#include "led.h"
#include "uart.h"
#include <math.h>
#include "systick.h"
volatile uint8_t temp;//定义全局变量,使得中断函数也能使用,使用的时候加extern
int main(void)
{
//uint8_t array[9] = {1, 10, 5, 15, 5,8, 0, 5, 3};
uint32_t i = 0;
int y;
//uint8_t str[10] = {'a','b','c'};
led_config();
USART1_Init(9600);
//GPIO_SetBits(GPIOB, GPIO_Pin_0|GPIO_Pin_1);
//uart_sendbyte(USART1, 'q');
//uart_sendhalfword(USART1, 0x56);
//uart_sendarray(USART1, str, 3);
//uart_sendstr(USART1, "hello word ");
//printf("hello word ");
// for(i = 0; i< 50;i++)
// {
// y = 100*sin(2*3.14*0.01*i);
// printf("%d ",y);
// SysTick_Delay_ms(100);
// }
while(1)
{
//GPIO_SetBits(GPIOB, GPIO_Pin_1);
y = 100*sin(2*3.14*0.01*i);
// USART_SendData(USART1, y); //发送单个数值
printf("%d", y);
SysTick_Delay_ms(100);
i++;
// switch(temp)
// {
// case '1':
// GPIO_SetBits(GPIOB, GPIO_Pin_0);
// GPIO_ResetBits(GPIOB, GPIO_Pin_1);
// break;
// case '2':
// GPIO_SetBits(GPIOB, GPIO_Pin_1);
// GPIO_ResetBits(GPIOB, GPIO_Pin_0);
// break;
// case '3':
// GPIO_SetBits(GPIOB, GPIO_Pin_0|GPIO_Pin_1);
// break;
// default:
// GPIO_ResetBits(GPIOB, GPIO_Pin_0|GPIO_Pin_1);
// break;
// }
}
}
Qt界面设计
这里用了一个正弦函数的值发送到串口端,我还写了一个用Qt写的界面还可视化这些数据。
ChartView.h文件
#pragma once
#include <QChartView>
#include <QMouseEvent>
#include <QGraphicsSimpleTextItem>
QT_CHARTS_USE_NAMESPACE
class ChartView : public QChartView
{
Q_OBJECT
public:
ChartView(QChart *chart, QWidget *parent = nullptr);
~ChartView();
// 保存坐标区域,用于复位
void saveAxisRange();
protected:
void mousePressEvent(QMouseEvent *event);
void mouseMoveEvent(QMouseEvent *event);
void mouseReleaseEvent(QMouseEvent *event);
void wheelEvent(QWheelEvent *event);
void keyPressEvent(QKeyEvent *event);
void keyReleaseEvent(QKeyEvent *event);
private:
QPoint m_lastPoint;
bool m_isPress;
bool m_ctrlPress;
bool m_alreadySaveRange;
double m_xMin, m_xMax, m_yMin, m_yMax;
QGraphicsSimpleTextItem* m_coordItem;
};
mainwindow.h文件
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include <QtCharts>
#include <QSerialPort>
#include <QSerialPortInfo>
#include <QTimer>
#include <QList>
#include <QVector>
#include <QDebug>
#include <QMouseEvent>
#include <QKeyEvent>
#include <QWheelEvent>
#include <QGraphicsSimpleTextItem>
#include "ChartView.h"
QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACE
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow(QWidget *parent = nullptr);
~MainWindow();
private:
Ui::MainWindow *ui;
QChart *chart;//定义绘图板
QValueAxis *xAixs;
QValueAxis *yAixs;//定义x和y轴
QSerialPort *serial;//定义串口
QLineSeries *serialLine;//定义Chart的线
//QTimer *timer;//定义定时器
QVector<double>vector;//定义接收数据容器
// void mousePressEvent(QMouseEvent *event);
// void mouseMoveEvent(QMouseEvent *event);
// void mouseReleaseEvent(QMouseEvent *event);
// void wheelEvent(QWheelEvent *event);
// void keyPressEvent(QKeyEvent *event);
//void timerEvent(QTimerEvent *e) override;
private slots:
void sys_init();//初始化界面函数
void send_data();//发送数据函数
void recv_data();//接收数据函数
void clear_data();//清除数据函数
void close_port();//关闭串口函数
void connect_port();//连接串口函数
//void TimerEvent();
};
#endif // MAINWINDOW_H
ChartView.cpp文件
#include "ChartView.h"
#include <QApplication>
#include <QValueAxis>
ChartView::ChartView(QChart *chart, QWidget *parent)
: QChartView(chart, parent)
{
m_isPress = false;
m_ctrlPress = false;
m_alreadySaveRange = false;
m_coordItem = nullptr;
this->setDragMode(QGraphicsView::RubberBandDrag);
this->setMouseTracking(false);
setCursor(QCursor(Qt::PointingHandCursor)); //设置鼠标指针为手指形
}
ChartView::~ChartView()
{
}
void ChartView::mousePressEvent(QMouseEvent *event)
{
if (event->button() == Qt::LeftButton)
{
m_lastPoint = event->pos();
m_isPress = true;
}
}
void ChartView::mouseMoveEvent(QMouseEvent *event)
{
if (!m_coordItem)
{
m_coordItem = new QGraphicsSimpleTextItem(this->chart());
m_coordItem->setZValue(5);
m_coordItem->setPos(100, 60);
m_coordItem->show();
}
const QPoint curPos = event->pos();
QPointF curVal = this->chart()->mapToValue(QPointF(curPos));
QString coordStr = QString("X = %1, Y = %2").arg(curVal.x()).arg(curVal.y());
m_coordItem->setText(coordStr);
if (m_isPress)
{
QPoint offset = curPos - m_lastPoint;
m_lastPoint = curPos;
if (!m_alreadySaveRange)
{
this->saveAxisRange();
m_alreadySaveRange = true;
}
this->chart()->scroll(-offset.x(), offset.y());
}
}
void ChartView::mouseReleaseEvent(QMouseEvent *event)
{
m_isPress = false;
if (event->button() == Qt::RightButton)
{
if (m_alreadySaveRange)
{
this->chart()->axisX()->setRange(m_xMin, m_xMax);
this->chart()->axisY()->setRange(m_yMin, m_yMax);
}
}
}
void ChartView::wheelEvent(QWheelEvent *event)
{
const QPoint curPos = event->pos();
QPointF curVal = this->chart()->mapToValue(QPointF(curPos));
if (!m_alreadySaveRange)
{
this->saveAxisRange();
m_alreadySaveRange = true;
}
const double factor = 1.5;//缩放比例
if (m_ctrlPress)
{//Y轴
QValueAxis *axisY = dynamic_cast<QValueAxis*>(this->chart()->axisY());
const double yMin = axisY->min();
const double yMax = axisY->max();
const double yCentral = curVal.y();
double bottomOffset;
double topOffset;
if (event->delta() > 0)
{//放大
bottomOffset = 1.0 / factor * (yCentral - yMin);
topOffset = 1.0 / factor * (yMax - yCentral);
}
else
{//缩小
bottomOffset = 1.0 * factor * (yCentral - yMin);
topOffset = 1.0 * factor * (yMax - yCentral);
}
this->chart()->axisY()->setRange(yCentral - bottomOffset, yCentral + topOffset);
}
else
{//X轴
QValueAxis *axisX = dynamic_cast<QValueAxis*>(this->chart()->axisX());
const double xMin = axisX->min();
const double xMax = axisX->max();
const double xCentral = curVal.x();
double leftOffset;
double rightOffset;
if (event->delta() > 0)
{//放大
leftOffset = 1.0 / factor * (xCentral - xMin);
rightOffset = 1.0 / factor * (xMax - xCentral);
}
else
{//缩小
leftOffset = 1.0 * factor * (xCentral - xMin);
rightOffset = 1.0 * factor * (xMax - xCentral);
}
this->chart()->axisX()->setRange(xCentral - leftOffset, xCentral + rightOffset);
}
}
void ChartView::keyPressEvent(QKeyEvent *event)
{
if (event->key() == Qt::Key_Control)
{
m_ctrlPress = true;
}
}
void ChartView::keyReleaseEvent(QKeyEvent *event)
{
if (event->key() == Qt::Key_Control)
{
m_ctrlPress = false;
}
}
void ChartView::saveAxisRange()
{
QValueAxis *axisX = dynamic_cast<QValueAxis*>(this->chart()->axisX());
m_xMin = axisX->min();
m_xMax = axisX->max();
QValueAxis *axisY = dynamic_cast<QValueAxis*>(this->chart()->axisY());
m_yMin = axisY->min();
m_yMax = axisY->max();
}
mainwindow.cpp文件
#include "mainwindow.h"
#include "ui_mainwindow.h"
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
{
ui->setupUi(this);
sys_init();//初始化界面
connect(ui->Data_Send,&QPushButton::clicked,this,&MainWindow::send_data);//按钮发送数据
connect(serial,&QSerialPort::readyRead,this,&MainWindow::recv_data);//串口接收数据
connect(ui->Data_Cls,&QPushButton::clicked,this,&MainWindow::clear_data);//按钮清除数据
connect(ui->Close_Serial,&QPushButton::clicked,this,&MainWindow::close_port);//按钮关闭串口
connect(ui->Connected_Serial,&QPushButton::clicked,this,&MainWindow::connect_port);//串口连接按钮
}
MainWindow::~MainWindow()
{
delete ui;
}
/*----------------------------------串口连接---------------------------------------------*/
void MainWindow::connect_port()
{
serial->open(QIODevice::ReadWrite);
ui->ConnectStatus->setText("已连接");
}
/*----------------------------------清除数据---------------------------------------------*/
void MainWindow::clear_data()
{
ui->PTE_Recv->clear();
ui->PTE_Send->clear();
ui->lineEdit->clear();
}
/*----------------------------------关闭串口---------------------------------------------*/
void MainWindow::close_port()
{
serial->close();
ui->ConnectStatus->setText("已断开");
}
/*----------------------------------发送数据---------------------------------------------*/
void MainWindow::send_data()
{
QByteArray SendData = ui->PTE_Send->toPlainText().toLatin1();
serial->write(SendData.data());
//ui->PTE_Recv->insertPlainText(ui->PTE_Send->toPlainText());//调试使用
//ui->PTE_Recv->insertPlainText(" ");//加空格
//qDebug()<<SendData.data();
}
/*----------------------------------接收数据----------------------------------------------*/
void MainWindow::recv_data()
{
QByteArray str2 = serial->readAll();//读取下位机的数据
ui->PTE_Recv->insertPlainText(str2);
//qDebug()<<str2;
ui->PTE_Recv->insertPlainText(" ");
//QByteArray info = info_mid.trimmed().data();
// QByteArray info_Hex = info_mid.toHex().trimmed().data();
chart = new QChart();//实例化画板对象
serialLine = new QLineSeries(this);//实例化线对象
/*--------数据填充-----------*/
//QString str1 = ui->PTE_Recv->toPlainText();
// ui->lineEdit->clear();
// //ui->lineEdit->setText(info);
// ui->lineEdit->setText(QString::fromLocal8Bit(info));
// QString str2 = ui->lineEdit->text();
// qDebug()<<"str2的数据:"<<str2;
// /*------------16进制的选择-----------------*/
// if(ui->Recv_Hex->isChecked())
// {
// ui->PTE_Recv->insertPlainText(QString().asprintf("%1X",str2.toInt()));
// ui->PTE_Recv->insertPlainText(" ");//加空格
// }
// else
// {
// //ui->PTE_Recv->insertPlainText(info);
// ui->PTE_Recv->insertPlainText(info);
// ui->PTE_Recv->insertPlainText(" ");
// }
vector.push_back(str2.toDouble());
for(int i = 0; i<vector.size();i++)
{
*serialLine<<QPointF(i,vector[i]);//将点放在线上
}
// QTimer *timer = new QTimer();
// connect(timer,&QTimer::timeout,[=](){
// update();
// *serialLine<<QPointF(vector.length()-1,vector[vector.length()-1]);//将点放在线上
// });//时间更新
// timer->start(1000);//间隔10ms更新一次
// qDebug()<<"vector的数据:"<<vector[vector.length()-1];
// qDebug()<<"vector的长度:"<<vector.length();
//qDebug()<<"info的长度:"<<info_int;
//qDebug()<<"10进制的info:"<<info_int;
// qDebug()<<"16进制的info:"<<i;
xAixs = new QValueAxis();
yAixs = new QValueAxis();//实例化轴对象
xAixs->setTitleText("数据个数");
//yAixs->setTickCount(5);
yAixs->setTitleText("数值");
xAixs->setTickCount(5);//设置刻度,10表示的是刻度的个数
//chart->createDefaultAxes();
chart->addSeries(serialLine);//将线放在图上
chart->legend()->hide();
chart->setTitle("实时波形显示");
chart->addAxis(xAixs,Qt::AlignBottom);//将轴放在图上
chart->addAxis(yAixs,Qt::AlignLeft);
serialLine->attachAxis(xAixs);
serialLine->attachAxis(yAixs);//将线依附在轴上
ui->graphicsView->setChart(chart);
}
/*----------------------------------初始化------------------------------------------------*/
void MainWindow::sys_init()
{
serial = new QSerialPort(this);//实例化串口
/*----------向ui的combox中添加可用的串口----------*/
QList<QSerialPortInfo> serialPortinfo = QSerialPortInfo::availablePorts();
int count = serialPortinfo.count();
for(int i = 0; i<count; i++)
{
ui->SE_Port->addItem(serialPortinfo.at(i).portName());//将可用的串口名字填入ComBox
serial->setPortName(serialPortinfo.at(i).portName());//设置串口对应的端口名字
}
// serial->setPortName("COM4");//设置串口对应的端口名字
/*--------向ui的combox中添加指定的波特率----------*/
QList<QString> BaudRate;
BaudRate<<"9600"<<"115200"<<"4800";//指定波特率位9600和115200
for (int i = 0;i<BaudRate.size();i++)
{
ui->Baud_Rate->addItem(BaudRate.at(i));
}
/*-----设置已经添加的波特率到指定的ComBox-------*/
if(ui->Baud_Rate->currentIndex() == 0)
{
serial->setBaudRate(QSerialPort::Baud9600);
}
else if(ui->Baud_Rate->currentIndex() == 1)
serial->setBaudRate(QSerialPort::Baud115200);
else
serial->setBaudRate(QSerialPort::Baud4800);
/*----------设置数据位-----------*/
QList<QString> DataBit;
DataBit<<"8"<<"7"<<"6"<<"5";
for(int i=0;i<DataBit.length();i++)
{
ui->Bit_Data->addItem(DataBit.at(i));
}
/*-----设置已经添加的数据位到指定的ComBox-------*/
if(ui->Bit_Data->currentIndex() == 0)
serial->setDataBits(QSerialPort::Data8);
else if(ui->Bit_Data->currentIndex() == 1)
serial->setDataBits(QSerialPort::Data7);
else if(ui->Bit_Data->currentIndex() == 2)
serial->setDataBits(QSerialPort::Data6);
else
serial->setDataBits(QSerialPort::Data5);
/*--------设置停止位----------*/
QList<QString> StopBit;
StopBit<<"1"<<"1.5"<<"2";
for(int i=0;i<StopBit.length();i++)
{
ui->Bit_Stop->addItem(StopBit.at(i));
}
/*------设置已经添加的停止位到指定的ComBox-------*/
if(ui->Bit_Stop->currentIndex() == 0)
serial->setStopBits(QSerialPort::OneStop);
else if(ui->Bit_Data->currentIndex() == 1)
serial->setStopBits(QSerialPort::OneAndHalfStop);
else
serial->setStopBits(QSerialPort::TwoStop);
/*----------设置校验位--------------*/
QList<QString> ParityBit;
ParityBit<<"无"<<"奇"<<"偶";
for(int i=0;i<ParityBit.length();i++)
{
ui->Bit_Parity->addItem(ParityBit.at(i));
}
/*------设置已经添加的校验位到指定的ComBox-------*/
if(ui->Bit_Parity->currentIndex() == 0)
serial->setParity(QSerialPort::NoParity);
else if(ui->Bit_Parity->currentIndex() == 1)
serial->setParity(QSerialPort::OddParity);
else
serial->setParity(QSerialPort::EvenParity);
}
Qt通过串口发送指令控制led灯
将main.c文件改成
#include "stm32f10x.h"
#include "led.h"
#include "uart.h"
#include <math.h>
#include "systick.h"
volatile uint8_t temp;//定义全局变量,使得中断函数也能使用,使用的时候加extern
int main(void)
{
//uint8_t array[9] = {1, 10, 5, 15, 5,8, 0, 5, 3};
uint32_t i = 0;
int y;
//uint8_t str[10] = {'a','b','c'};
led_config();
USART1_Init(9600);
//GPIO_SetBits(GPIOB, GPIO_Pin_0|GPIO_Pin_1);
//uart_sendbyte(USART1, 'q');
//uart_sendhalfword(USART1, 0x56);
//uart_sendarray(USART1, str, 3);
//uart_sendstr(USART1, "hello word ");
//printf("hello word ");
// for(i = 0; i< 50;i++)
// {
// y = 100*sin(2*3.14*0.01*i);
// printf("%d ",y);
// SysTick_Delay_ms(100);
// }
while(1)
{
// y = 100*sin(2*3.14*0.01*i);
// printf("%d", y);
// SysTick_Delay_ms(100);
// i++;
switch(temp)
{
case '1':
GPIO_SetBits(GPIOB, GPIO_Pin_0);
GPIO_ResetBits(GPIOB, GPIO_Pin_1);
break;
case '2':
GPIO_SetBits(GPIOB, GPIO_Pin_1);
GPIO_ResetBits(GPIOB, GPIO_Pin_0);
break;
case '3':
GPIO_SetBits(GPIOB, GPIO_Pin_0|GPIO_Pin_1);
break;
default:
GPIO_ResetBits(GPIOB, GPIO_Pin_0|GPIO_Pin_1);
break;
}
}
}
中断服务函数:
extern uint8_t temp;
//串口1中断服务程序(固定的函数名不能修改)
int USART1_IRQHandler(void)//串口1中断服务程序(固定的函数名不能修改)
{
while(USART_GetFlagStatus(USART1, USART_FLAG_RXNE) == RESET);
temp = USART_ReceiveData(USART1);
// USART_SendData(USART1,temp);
printf("%d",temp);
return temp;
}
这样就可以控制led的亮灭。