QT串口与51单片机通信通过这个小例子主要想说明QT怎样进行线程编程的思想,实例如图,好吧,下面是过程上一个例子我们采用的是手工编写代码的方法,这个例子我们来玩一下designer,其实Qt4己经把界面与功能分开了,用designer来进行界面设计,再手工编写一些功能,如信号与槽,这样开发效率会大大提高,呵呵,开一个终端,输入/usr/local/Trolltech/Qt-4.5.1/bin/designer,如果第一次打开出现字体不对,可以打开qtconfig进行一些相关配置,打开后我们新建一个Main Window,在右边的属性框中设置一下界面大小,1.我ARM板的LCD大小为320x240,所以我也设为320x240;2.左边是一些我们常用的窗口部件,这里我们用到一个lable标签来做显示,再放几个pushButton按钮,在属性objectName重新更改它的名字,改为我们记得的,这样在写功能时记得哪个按钮叫什么名字,对于一个初学QT的人来说,很想知道每一个部件到底有什么信号和槽,别急,我们可以这样来看,选中一个lable,按F4,再点击lable拖动出现接地符号时松开,弹出编辑信号与槽,这时左边列出的是信号,右边为槽,这里我们不用配置连接,等下我们再手工写,3最后我们用到一个lable标签和三个pushButton按钮,并命名为dis_label、writeButton、readButton、closeButton,然后保存为mainwindow.ui,这样designer就完工了,呵呵..4.下面我们编写一个线程,用于管理串口收发工作,它不涉及到任何界面,只做好它的本份工作就得了,编写一个thread.h文件gedit thread.h,#ifndef THREAD_H#define THREAD_H#include<QThread>class Thread:public QThread{Q_OBJECTpublic:Thread();char buf[128];volatile bool stopped;volatile bool write_rs;volatile bool read_rs;protected:virtual void run();};#endif我们定义一个Thread类,它继承于QThread,看到只设有一些变量和一个run函数,virtual表示为虚函数,你也可以去掉,加上去会增加一些内存开销,但提高了效率,对于这个小程序是看不出什么效果的,volatile这个大家都懂了吧,就是防止偷懒,呵呵,5.再看看thread.cpp#include"thread.h"#include <sys/types.h>#include <sys/stat.h>#include <fcntl.h>#include <termios.h> //串口用到的#include <stdio.h>#include <stdlib.h>#include <unistd.h>#include <strings.h>#define BAUDRATE B9600//#define RS_DEVICE "/dev/ttyS0" //串口1#define RS_DEVICE "/dev/ttySAC1" //串口1Thread::Thread(){} //析构void Thread::run() //这就是线程的具体工作了int fd,c=0,res;struct termios oldtio,newtio; //termios结构是用来保存波特率、字符大小等printf("start...\n");fd=open(RS_DEVICE,O_RDWR|O_NOCTTY); //以读写方式打开串口。
不控制TTYif(fd<0){perror("error");exit(1); //失败退出}printf("Open...\n");tcgetattr(fd,&oldtio); //保存当前设置到oldtiobzero(&newtio,sizeof(newtio)); //清除newtio结构,并重新对它的成员设置如下newtio.c_cflag=BAUDRATE|CS8|CLOCAL|CREAD; //9600、8位、忽略DCD信号、启用接收装置newtio.c_iflag|=IGNPAR; //忽略奇偶newtio.c_oflag=0;newtio.c_lflag=0;newtio.c_cc[VMIN]=0;newtio.c_cc[VTIME]=100; //在规定时间(VTIME)内读取(VMIN)个字符;tcflush(fd,TCIFLUSH); //清除所有队列在串口的输入与输出;tcsetattr(fd,TCSANOW,&newtio); //把我们的设置写入termioswhile(stopped) //stopped为0时将退出线程{if(write_rs) //write_rs为1时把字符串从串口中输出{write_rs=0;write(fd,"QtEmbedded-4.5.1",16);}if(read_rs) //read_rs为1时读取,并存在buf{read_rs=0;res=read(fd,buf,10);buf[res]=0;emit finished(); //读完后发一个信号}}printf("Close...\n");tcsetattr(fd,TCSANOW,&oldtio); //重新设置回原来的close(fd);quit();}QT有terminated()和wait函数来停止或暂停线程,为什么不用呢,具书上说会造成阻塞什么的,呵呵,我也不懂,所以我就用stopped变量来控制,让它死循环在那里,其它我已经在注释上说明了,应该没什么问题了吧,没有的话再写一个.6.mainwindow.h#ifndef MAINWINDOW_H#define MAINWINDOW_H#include<QtGui>#include"ui_mainwindow.h" //奇怪?这个头文件从哪里来的?呵呵,刚才用designer做的mainwindow.ui文件,经过make后会生成这个头文件,#include"thread.h" //把我们前面定义的线程包含进来class MainWindow:public QMainWindow,public Ui::MainWindow //多继承{Q_OBJECTpublic:MainWindow(QWidget *parent=0);public slots:void writeThread();void readThread();void closeThread();void display();private:Thread *yy;};#endifMainWindow继承于QMainWindow和MainWindow,即多继承,对于不是很复杂的程序,用多继承是一个较好的方法,如果程序较复杂,还是用单继承了,我们再看一下mainwindow.cpp,看这些槽具有什么功能:#include"mainwindow.h"MainWindow::MainWindow(QWidget *parent):QMainWindow(parent){setupUi(this);yy = new Thread;yy->start(); //启动线程yy->stopped=1; //初始化变量yy->write_rs=0;yy->read_rs=0;connect(writeButton,SIGNAL(clicked()),this,SLOT(writeThread())); //信号与槽connect(readButton,SIGNAL(clicked()),this,SLOT(readThread()));connect(closeButton,SIGNAL(clicked()),this,SLOT(closeThread()));connect(yy,SIGNAL(finished()),this,SLOT(display())); //前面线程读完了不是发一个信号么,这个信号就是发到这个槽}void MainWindow::display(){dis_label->setText(yy->buf); //读到的在dis_label显示,dis_label就是我们前面designer放的标签,}void MainWindow::writeThread() //前面线程都是根据stopped、write_rs、read_rs的状态来工作的^_^{yy->write_rs=1;}void MainWindow::readThread(){yy->read_rs=1;}void MainWindow::closeThread(){yy->stopped=0;}还差个main.cpp就完工了,哈哈...#include<QApplication>#include"mainwindow.h"int main(int argc,char *argv[]){QApplication app(argc,argv);MainWindow mw;mw.show();return app.exec();}啊!终于完成了,不!这只是完成了ARM这边,还有MCU这边,呵呵,很久不玩单片机了,下面为AT89S51编一个串口程序,并用1602来显示:/**************************************************************//* 三贱客龙& 07机电(1)班*//* 目标器件AT89S51 *//* 晶振11.0592 MHZ *//* 编译环境keil uVsion3 *//* 适用于51综合开发实验板*//**************************************************************//********************** 1602 ********************************//**************一定要关了上拉电阻再下载,有干拢****************/#include <reg51.h>#include <intrins.h>typedef unsigned char BYTE; //类型定义,编译时再处理typedef bit BOOL ; //位/****************串口部份**********************************/unsigned char key_s, key_v, tmp;sbit K1 = P1^4;BOOL flag1=0;BYTE i=0;BYTE z=0;void isr_uart(void); //串口中断服务函数void send_str(); // 传送字串bit scan_key(); // 扫描按键void proc_key(); // 键处理void delayms(unsigned char ms);com_init() //串口初始部分{TMOD = 0x20; // 定时器1工作于8位自动重载模式, 用于产生波特率方式2TH1 = 0xFD; // 波特率9600TL1 = 0xFD;SCON = 0x50; // 设定串行口工作方式为1且没有校验位PCON=0X00; // 波特率不倍增,倍增时为0x80TR1 = 1; // 启动定时器1运行ET1 = 0; // 禁T1定时器产生中断ES=1; //允串口中断EA=1; //开总中断}/****************************串口部分************************************/sbit rs = P2^0; // 寄存器选择sbit rw = P2^1; //读写信号线sbit ep = P2^2; //使能端sbit right = P2^6; //背光char idata display[16] = {"zhilong2382@163"}; //开机时显示的字符串char code str[] = {"xxxI Love you ! "}; //按K1时将发送这个内容,由于前3位对方总是收不到,这里用xxx干掉了,用串口调试助手也一样,delay(BYTE ms){ // 延时子程序BYTE i;while(ms--){for(i = 0; i< 250; i++){_nop_();_nop_();_nop_();_nop_();}}}BOOL lcd_bz() //只检测最高位{ // 测试LCD忙碌状态BOOL result;rs = 0; //指令寄存器rw = 1; //读ep = 1; //接收指令,为下降沿做准备_nop_(); //空语句,延时约1US_nop_();_nop_();_nop_();result = (BOOL)(P0 & 0x80); //取最高位ep = 0; //执行命令return result;}/************** 写入指令数据到LCD **************/lcd_wcmd(BYTE cmd){while(lcd_bz()); //忙时执行空语句rs = 0;rw = 0;ep = 0;_nop_();_nop_();P0 = cmd;_nop_();_nop_();_nop_();_nop_();ep = 1;_nop_();_nop_();_nop_();_nop_();ep = 0;}lcd_pos(BYTE pos){ //设定显示位置lcd_wcmd(pos | 0x80); //指令8}lcd_wdat(BYTE dat) //写入字符显示数据到LCD { //写入要显示的字符串while(lcd_bz()); //读忙rs = 1; //写数据命令rw = 0;ep = 0;P0 = dat;_nop_();_nop_();_nop_();_nop_();ep = 1;_nop_();_nop_();_nop_();_nop_();ep = 0;}lcd_init(){ //LCD初始化设定lcd_wcmd(0x38); //指令6,不检测忙信号delay(1);lcd_wcmd(0x0c); //指令4,显示开/关控制delay(1);lcd_wcmd(0x06); //指令3,输入模式delay(1);lcd_wcmd(0x01); //清除LCD的显示内容delay(1);}/*****************************************main********************************/ main(){BYTE i,j; j=0;right=0; //开背光lcd_init(); // LCDdelay(10);com_init(); // 初始化串口/*************** 显示内容及方式*********************/while(1){if(scan_key()) // 扫描按键第一遍为真{delayms(10); // 延时去抖动if(scan_key()) // 再次扫描{key_v = key_s; // 保存键值proc_key(); // 键处理}}lcd_wcmd(0x01); //清除LCD的显示内容delay(1);lcd_pos(0); // 设置显示位置为第一行的第3个字符i = 0;while(display[i] != '\0') //是否到字符串末尾{ // 显示字符lcd_wdat(display[i]);i++; delay(70);}}}// 扫描按键bit scan_key(){key_s = 0x00;key_s |= K1; //字节与位运算改变的是最低位return(key_s ^ key_v);}// 键处理void proc_key(){if((key_v & 0x01) == 0){ // K1按下TI=1; //send_str(); // 传送字串到PC}}// 每次传送完一串字符void send_str(){unsigned char i = 0;while(str[i] != '\0'){SBUF = str[i];while(!TI); // 等特数据传送,发完后TI=1TI = 0; // 清除数据传送标志i++; // 下一个字符}}// 延时子程序void delayms(unsigned char ms){unsigned char i;while(ms--){for(i = 0; i < 120; i++);}}void isr_uart(void) interrupt 4 //串口服务函数{if(RI==1){ RI=0;display[z]=SBUF;z++;if(z>=16)z=0;}if(TI==1)send_str();}呵呵,这个是我大一时学单片机时弄的,看起来乱乱的,但我保证绝对能用,程序编译后把hex 文件烧到单片机,插上串口线,串口线我是自己做的,注意有直通与交叉之分,一切准备好后,通电...怎么样,当在ARM板上点击write按钮时,将发送QtEmbedded-4.5.1给单片机,并在1602上显示出来,点read后,再按下p1.4键时(按久点,因为我是用查询方法读键),单片机发送xxxI Love you !给ARM,ARM板上显示I Love you !,怎么样,是不是更爽了,是的话我们再编一个复杂点的例子,编什么呢?PWM吧,可以用来控制电机的转速,我是学机电的,也就是我的专业是机电一体化,是不是很奇怪学机电的也搞程序?哈哈..兴趣!!这个例子为在QT上可以改变频率、占空比,并有语音提示,还专门编了个数字小键盘方便输入,代码下次再贴了。