发新帖本帖赏金 1.00元(功能说明)我要提问
123下一页
返回列表
打印

【21ic分享】串口助手显示汉字的一点小思

[复制链接]
7730|46
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
本帖最后由 冰零分子 于 2016-6-8 14:08 编辑

看到21ic分享赛手又痒痒了,可是思来想去又没有什么好的idea,

从毕业后也很少自己做过东西了,以前的呢早就抛到九霄云外了,

遗憾啊,刚巧前段时间用串口助手发现点问题,不能显示汉字。

看了看是是龚建伟老师编写的v2.2版,

正巧之前看过龚老师的Visual C++/Turbo C串口通讯编程实践教程,

于是乎就把尘封已久的源码挖了出来........也算是种分享吧^_^,

所以呢题目中“赛”字就没有了,

介于本程序稍加修改还可以改造出适合自己的调试小工具,

所以就当是为小伙伴们助力了



打赏榜单

gaochy1126 打赏了 1.00 元 2016-06-12
理由:请继续努力

评分
参与人数 1威望 +10 收起 理由
ocon + 10 赞一个!

相关帖子

沙发
冰零分子|  楼主 | 2016-6-8 11:42 | 只看该作者
写这篇帖子呢还有个目的:

对汉字显示处理的部分遇到点问题希望前辈们指点指点

话说龚老师就是因串口助手红遍了大江南北,

因此此程序由龚建伟老师的Visual C++/Turbo C串口通讯编程实践教程而来,

简要说下后果前因,下面切入正题吧………………………………………………

使用特权

评论回复
板凳
冰零分子|  楼主 | 2016-6-8 11:45 | 只看该作者
此串口助手用VC++编译器编译,使用了MFC,因此,

欲练此功,必先。。。。。额这不是什么宝典,所以会点MFC基本操作就行了

那什么是MFC呢,(此处省略n个字),由于篇幅限制本次仅介绍串口收发核心部分。

首先新建一个MFC AppWizard(exe)基于对话框的工程,工程名为SCOMM。

VC++说了:“我把这个人的整体骨骼框架制作好了,你就在上面添鼻子加眼吧”

对,有了骨架了,这个人的外形基本就出来了,但这个人比较特殊,仅会听/说就行了,

所以也不需要添鼻子加眼了,添耳朵加嘴就行啦

使用特权

评论回复
地板
冰零分子|  楼主 | 2016-6-8 11:58 | 只看该作者
本帖最后由 冰零分子 于 2016-6-8 12:28 编辑

提前露一下真身:

SCOMM_icy.rar

429.78 KB

应用程序

使用特权

评论回复
5
冰零分子|  楼主 | 2016-6-8 12:30 | 只看该作者
按龚老师的命名方式为个控件添加变量,命名本身就是注释,根据命名很好分辨哪个控件,

先不列出了,稍后会放出教程,自己对着找吧,^_^
这是我的布局,基本没怎么变


使用特权

评论回复
6
冰零分子|  楼主 | 2016-6-8 12:32 | 只看该作者
本帖最后由 冰零分子 于 2016-6-8 14:31 编辑

由于本人VC并不精通,所以专挑软柿子捏,就选择用SerialPort类来完成此次工作,

既然要用SerialPort类,那就要把别人写好的SerialPort.cpp和SerialPort.h文件加入到当前工程。

另外还要在你建立工程对话框的头文件(本工程为SCOMMDlg.h)中包含下头文件#include "SerialPort.h"。

开始之前,先看一下这家伙有哪几把刷子(protected成员就算了,那是人家自己用的),主要看咱要的,

BOOL InitPort(CWnd* pPortOwner, UINT portnr = 1, UINT baud = 19200,
                char parity = 'N', UINT databits = 8, UINT stopbits = 1,
                DWORD dwCommEvents = EV_RXCHAR, UINT writebuffersize = 1024);

串口的初始化,你们交谈前先协商好语速,怎么发现说错话,一次说几个单词之类的。。。

BOOL                StartMonitoring();

BOOL                StopMonitoring();启动/停止串口通信检测线程,这是耳朵干的事。。

void                WriteToPort(char* string);向端口写函数,当然这就是嘴干的事了,

后面对此函数又进一步完善,

        void                WriteToPort(char* string,int n);
        void                WriteToPort(LPCTSTR string);
        void                WriteToPort(LPCTSTR string,int n);

多了这三兄弟,c++的重载知识,按下不表,这三胞胎兄弟名字都是一样,但他们爱好不一样,

你给他们电影票老大就主动跑出来看电影去了,给个演唱会门票老二就抢着跑去了

当然还有一个重要的消息#define WM_COMM_RXCHAR        WM_USER+7在此消息队列里他排行老七。

耳朵听到一个单词他会立即告诉大脑。。。。

好拉差不多也就这么多了啦。

使用特权

评论回复
7
冰零分子|  楼主 | 2016-6-8 12:33 | 只看该作者
本帖最后由 冰零分子 于 2016-6-8 12:39 编辑

想要交流咱就先约定一下规则吧,

你要和他默认约定一下规则的话就在OnInitDialog()中初始化串口,

        m_Com.SetCurSel(0);
        m_Speed.SetCurSel(5);
        m_Parity.SetCurSel(0);
        m_DataBits.SetCurSel(0);
        m_StopBits.SetCurSel(0);
        
        m_bOpenPort = FALSE;
        m_nBaud=9600;
        m_nCom=1;
        m_cParity='N';
        m_nDatabits=8;
        m_nStopbits=1;
        m_dwCommEvents = EV_RXFLAG | EV_RXCHAR;

        if (m_Port.InitPort(this, m_nCom,                 

m_nBaud,m_cParity,m_nDatabits,m_nStopbits,m_dwCommEvents,512))
        {
                m_bOpenPort = TRUE;
                m_ctrlOpenPort.SetWindowText("关闭串口");
                m_Port.StartMonitoring();
                strStatus.Format("STATUS:COM%d OPENED,%d,%c,%d,%d",m_nCom,                 

m_nBaud,m_cParity,m_nDatabits,m_nStopbits);
                m_ctrlIconOpenoff.SetIcon(m_hIconRed);
                //m_ctrlIconOpenoff.SetIcon(m_hIconOff);

                //"当前状态:串口打开,无奇偶校验,8数据位,1停止位");
        }
        else
        {
                m_bOpenPort = FALSE;
                m_ctrlOpenPort.SetWindowText("打开串口");
                AfxMessageBox("没有发现此串口");
                m_ctrlIconOpenoff.SetIcon(m_hIconOff);
        }还有就是你来告诉他规则然后说:“hi小串,注意我要说话了(打开串口)”,

双击打开/关闭串口按钮控件添加响应函数

void CSCOMMDlg::OnButtonOpenport()
{
        //处理都差不多,先看教程吧^_^
}

使用特权

评论回复
8
冰零分子|  楼主 | 2016-6-8 12:40 | 只看该作者
现在我们来完善下听的功能,当耳朵听到一个单词后大脑需要对这个单词做些处理,

所以大脑需要一个能够处理单词的功能,先来告诉一下大脑,你有这个处理单词的功能了。

大脑是怎么知道他都有哪些功能的呢,功能就是在SCOMMDlg.h文件添加函数声明如下:       
// Generated message map functions
        //{{AFX_MSG(CSCOMMDlg)
        afx_msg LONG OnCommunication(WPARAM ch, LPARAM port);
        //}}AFX_MSG

此时加到此处说明大脑就知道有此功能模块了,消息怎么才能找到此功能的模块呢?

我们知道映射就是从A到B的一个过程,那就给此消息到模块来个映射把,

在SCOMMDlg.cpp文件中添加消息映射如下:

BEGIN_MESSAGE_MAP(CSCOMMDlg, CDialog)
        //{{AFX_MSG_MAP(CSCOMMDlg)
        ON_MESSAGE(WM_COMM_RXCHAR, OnCommunication)
        //}}AFX_MSG_MAP
END_MESSAGE_MAP()

接下里就看下这个模块的具体实现吧,当然他听到了还不算完他还要写下来给你看,

显示编辑框就是他的笔记本^6^

使用特权

评论回复
9
冰零分子|  楼主 | 2016-6-8 12:44 | 只看该作者
LONG CSCOMMDlg::OnCommunication(WPARAM ch, LPARAM port)
{
        if (port <= 0 || port > 4)
                return -1;
        rxdatacount++;   //接收的字节计数
        CString strTemp;
        strTemp.Format("%ld",rxdatacount);
        strTemp="RX:"+strTemp;
        m_ctrlRXCOUNT.SetWindowText(strTemp);  //显示接收计数
       
        if(m_bStopDispRXData)   //如果选择了“停止显示”接收数据,则返回
                return -1;          //注意,这种情况下,计数仍在继续,只是不显示
        //若设置了“自动清空”,则达到50行后,自动清空接收编辑框中显示的数据
        if((m_ctrlAutoClear.GetCheck())&&(m_ctrlReceiveData.GetLineCount()>=50))
        {
                m_ReceiveData.Empty();
                UpdateData(FALSE);
        }
        //如果没有“自动清空”,数据行达到400后,也自动清空
        //因为数据过多,影响接收速度,显示是最费CPU时间的操作
        if(m_ctrlReceiveData.GetLineCount()>400)
        {
                m_ReceiveData.Empty();
                m_ReceiveData="***The Length of the Text is too long, Emptied Automaticly!!!       

        ***\r\n";        
                UpdateData(FALSE);
        }

        //原教程为如下内容,不能显示汉字

        //如果选择了"十六进制显示",则显示十六进制值
        CString str;
        if(m_ctrlHexReceieve.GetCheck())
                str.Format("%02X ",ch);
        else
                str.Format("%c",ch);
        //以下是将接收的字符加在字符串的最后,这里费时很多
        //但考虑到数据需要保存成文件,所以没有用List Control
        int nLen=m_ctrlReceiveData.GetWindowTextLength();
        m_ctrlReceiveData.SetSel(nLen, nLen);
        m_ctrlReceiveData.ReplaceSel(str);
        nLen+=str.GetLength();

        m_ReceiveData+=str;

        return 0;       
}

使用特权

评论回复
10
冰零分子|  楼主 | 2016-6-8 12:46 | 只看该作者
本帖最后由 冰零分子 于 2016-6-8 12:47 编辑

他可以根据你的需求写下十六进制和ASCII显示,就是上面显示部分没有加入汉字的处理,

所以他不能显示汉字,上面是单字节显示的,听到一个字节就把写在笔记本上句子的末尾,

因此你让他原封不动的把听到话写给你看(十六进制显示)可以。

经搜索汉字是双字节显示的,并且字节>0x80,

你要让他写出ASCII码的时候他是单字节写的,他只知道128个所以他就会写这么多,

当你说的字节大于0x80时,由于超出了他的常识,

所以他稀里糊涂的写了但你就是不知道他写了啥了…………,

因此这个事你需要告诉他,拆开了不认识,合起来他就认识了,

简单点在函数最后刷新下对话框就行了        UpdateData(FALSE);

但这样的话下面
        
        int nLen=m_ctrlReceiveData.GetWindowTextLength();
        m_ctrlReceiveData.SetSel(nLen, nLen);
        m_ctrlReceiveData.ReplaceSel(str);
        nLen+=str.GetLength();

这一大堆工作就白做了,好不容易把汉字听一个写一个的加最后了,你这一刷新不要紧,

我前面写的还要重新写一遍,少的话还看不出什么,数据一旦超出一页多他就火了,一会翻到尾页,

一会又翻到首页,一直写的话你就会看到垂直滚动条一直上下乱窜,文字闪烁的晃眼

加个帧尾或填个简单的协议?那就不是串口助手的功能的作用了,本身就是所见即所得的,加协议肯定不行。

接收一段后一块显示也可以,不过我让他听到一个就写出一个,这样他才他本来的样子,

使用特权

评论回复
11
冰零分子|  楼主 | 2016-6-8 12:54 | 只看该作者
于是乎下面的改进出来了:先开辟一个缓存

        CString hz_rxdata;//用于暂存汉字的第一个字节
       
将显示部分改为:
        CString str=_T("");
        if(m_ctrlHexReceieve.GetCheck())               
        {
                m_bHZ = FALSE;
                hz_rxdata.Empty();
                str.Format("%02X ",ch);        //十六进制显示
        }
        else                                               
        {
                if(ch>=0x80)
                {
                        m_bHZ = !m_bHZ;
                        str.Format("%c",ch);
                        hz_rxdata+=str;
                        if(!m_bHZ)       
                        {       
                                str = hz_rxdata;
                                hz_rxdata.Empty();
                        }
                       
                }   
                else       
                {                                       
                        m_bHZ = FALSE;
                        hz_rxdata.Empty();      
                        str.Format("%c",ch);
                }
               
        }

        //将接受的字符加在字符串的最后,这里耗时很多
        //考虑到数据需要保存到文件,所以没有使用list control
        if(!m_bHZ)
        {
                int nLen=m_ctrlReceiveData.GetWindowTextLength();
                m_ctrlReceiveData.SetSel(nLen, nLen);                //定位光标到最后
                m_ctrlReceiveData.ReplaceSel(str);                //替换光标处文本
                nLen+=str.GetLength();
                m_ReceiveData+=str;
        }

使用特权

评论回复
12
冰零分子|  楼主 | 2016-6-8 13:04 | 只看该作者
大于0x80不认识不要紧,想让他存起来,等下一个大于0x80的字节来组合起来他就认识了,

下一个他本身就认识(<0x80)?那就直接把上次的扔了就行了^_^,

好了,看似是解决了,测试一下吧(这个是在工程完成后测试的),哗哗哗,给他讲了一大堆,

嘿,听明白了,可以正常显示了,不放心,来个对话,他说0x01,回复123456789,

OK正常

使用特权

评论回复
13
冰零分子|  楼主 | 2016-6-8 13:07 | 只看该作者
当然他们是这么对话的:

使用特权

评论回复
14
冰零分子|  楼主 | 2016-6-8 13:11 | 只看该作者
都OK,汉字字母数字走一遍






使用特权

评论回复
15
冰零分子|  楼主 | 2016-6-8 13:12 | 只看该作者
都OK,再来个单个汉字



使用特权

评论回复
16
冰零分子|  楼主 | 2016-6-8 13:14 | 只看该作者
OK,发个12:

使用特权

评论回复
17
冰零分子|  楼主 | 2016-6-8 13:31 | 只看该作者
来个单独数字1



不正常了,单个a,b存在同样的问题,
可以看到我第二个图tx从6发到了11,仅仅收到了两个1

使用特权

评论回复
18
冰零分子|  楼主 | 2016-6-8 13:33 | 只看该作者
本帖最后由 冰零分子 于 2016-6-8 13:36 编辑

这也是我想要请教前辈的一个问题

就是说他存在接收单个字节的时候存在一定概率接收不到,

所以这样处理还是存在一定问题,如果是用API的话,


API提供有一个COMMTIMOUTS结构体可以设置接收字符超时时间(没具体研究过),

这种方法应该就是常规的接收处理方法,


不过在用SerialPort类的时候这个应该怎么处理?望前辈多多指点





使用特权

评论回复
19
冰零分子|  楼主 | 2016-6-8 13:37 | 只看该作者
这是听得部分,下面就是说的部分了,说就比较简单,你给他简单指示一下说什么,他直接原话照搬就行了

也不需要他理解到底说的是什么……当然还可以指定他以十六进制发送,下面就是说的过程:

同样为手动发送添加响应函数:

void CSCOMMDlg::OnButtonManualsend()
{
        // TODO: Add your control notification handler code here
        if(m_Port.m_hComm==NULL)
        {
                m_ctrlAutoSend.SetCheck(0);
                AfxMessageBox("串口没有打开,请打开串口");
                return;
        }
        else
        {
                UpdateData(TRUE);//发送前更新一下你的指示
               
                if(m_ctrlHexSend.GetCheck())//十六进制发送
                {
                        char data[512];
                        int len=Str2Hex(m_strSendData,data);
                        m_Port.WriteToPort(data,len);
                        TX_count+=(long)((m_strSendData.GetLength()+1)/3);
                        //m_Port.WriteToPort(hexdata);       
                }
                else
                {
                        m_Port.WriteToPort((LPCTSTR)m_strSendData);        //发送数据
                        TX_count+=m_strSendData.GetLength();
                }
                CString strTemp;
                strTemp.Format("TX:%d",TX_count);
                m_ctrlTXCount.SetWindowText(strTemp);
        }
}

使用特权

评论回复
20
冰零分子|  楼主 | 2016-6-8 13:44 | 只看该作者
好了,致此核心部分就这些了,教程和源码都上传到下载站了,附上地址
Visual C++/Turbo C串口通讯编程
串口助手V2.2源码

使用特权

评论回复
发新帖 本帖赏金 1.00元(功能说明)我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

个人签名:每天进步一点点

15

主题

950

帖子

19

粉丝