分类:
2008-10-13 16:51:13
现在很多笔记本都不带有标准串口,但在工作环境中又需要使用串口,在window下我们还可以使用USB转RS232设备来将就一下,但这种设备带的驱动程序一般都只有window的,偶尔也有提供Linux的,但由于linux下的驱动要根据linux的内核版本来区分安装,厂商一般都很难根据linux的发展来及时更新,如果要碰到驱动和自己使用的linux内核版本不同会是一件很麻烦的事。最近发现使用VMware这个变通的方法可以解决这个问题,拿出来和大家共享一下:)。
说起来很简单,首先机器上要有window操作系统,一个USB转RS232设备(当然在window下能正常使用),然后安装VMware,在虚拟环境中安装一个linux系统,准备工作就这些了,然后进行一下简单的设置,让VMware来接管window的串口。选择VM-〉Settings,在Hardware属性页中点击Add,根据提示选择增加一个Serial Port设备,选择window下USB转RSR232设备转换后的串口号,就OK了。在虚拟的linux中接管的串口一般都是/dev/ttyS0。
Linux串口编程的一些问题解决
Linux下串口编程的文章网上是满天飞,但大都是出自一篇文章,而且写的都是些基本的操作,像控制RTS/CTS等串口引脚状态,接收发送二进制数据等,都没有很好的说明,我在使用中遇到了些问题,写出来,希望能对大家有所帮助,少走弯路,呵呵!
我使用的操作系统是Redhat9,gcc版本是3.2.2
其实在linux下对串口的设置主要是通过termios这个结构体实现的,但是这个结构体却没有提供控制RTS或获得CTS等串口引脚状态的接口,可以通过ioctl系统调用来获得/控制。
获得:
ioctl(fd, TIOCMGET, &controlbits);
if (controlbits & TIOCM_CTS)
printf(“有信号\n”);
else
printf(“无信号\n”);
设置:
ioctl(fd, TIOCMGET, &ctrlbits);
if (flag)
ctrlbits |= TIOCM_RTS;
else
ctrlbits &= ~TIOCM_RTS;
ioctl(fd, TIOCMSET, &ctrlbits);
其实TIOCM_RTS有效后是把串口的RTS设置为有信号,但串口的电平为低时是有信号,为高时为无信号,和用TIOCMGET获得的状态正好相反,也就是说TIOCMGET/TIOCMSET只是获得/控制串口的相应引脚是否有信号,并不反应当前串口的真实电平高低。
网上许多流行的linux串口编程的版本中都没对c_iflag(termios成员变量)这个变量进行有效的设置,这样传送ASCII码时没什么问题,但传送二进制数据时遇到0x0d,0x11和0x13却会被丢掉。不用说也知道,这几个肯定是特殊字符,被用作特殊控制了。关掉ICRNL和IXON选项即可解决。
c_iflag &= ~(ICRNL | IXON);
0x0d 回车符CR
0x11 ^Q VSTART字符
0x13 ^S VSTOP字符
ICRNL 将输入的CR转换为NL
IXON 使起动/停止输出控制流起作用
在《UNIX环境高级编程 第二版》第18章第11小节看到把终端I/O设置为原始模式(串口通讯就是终端I/O的原始模式)时输入属性设置为
term.c_iflag &= ~(BRKINT | ICRNL | INPCK | ISTRIP | IXON);
屏蔽了许多属性,怪不得有人说如果是使用串口通讯c_iflag和c_oflag都设置为0就行了!
以下是我的设置的一些重要的串口属性
term.c_cflag |= CLOCAL | CREAD;
term.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);
term.c_oflag &= ~OPOST;
term.c_iflag &= ~(BRKINT | ICRNL | INPCK | ISTRIP | IXON);
Yaffs2的ecc校验
在S3C2410的一个板子上移植内核2.6.14和yaffs2文件系统,但ecc校验总是设置不对,总是警告未使用ecc校验,内核配置如下
Device Drivers --->
Memory Technology Devices (MTD) --->
NAND Flash Device Drivers --->
· NAND Device Support
[ ] Verify NAND page writes
· NAND Flash support for S3C2410/S3C2440 SoC
· S3C2410 NAND driver debug
[ ] S3C2410 NAND Hardware ECC
File systems --->
Miscellaneous filesystems --->
· YAFFS2 file system support
--- 512 byte / page devices
· Lets Yaffs do its own ECC
· Use the same ecc byte order as Steven Hill's nand_ecc.c
--- 2048 byte (or larger) / page devices
· Autoselect yaffs2 format
· Disable lazy loading
· Turn off wide tnodes
· Turn off debug chunk erase check
· Cache short names in RAM
在drivers/mtd/nand/s3c2410.c中使用chip->eccmode = NAND_ECC_SOFT;
kernel启动没有出现警告,通过nfs启动后,安装文件系统
mount -t yaffs /dev/mtdblock/3 /mnt
cd mnt
tar xvzf /fa-yaffs-v5.tgz
然后就出现如下警告
./
Reading data from NAND FLASH without ECC is not recommended
Writing data without ECC to NAND-FLASH is not recommended
Writing data without ECC to NAND-FLASH is not recommended
Writing data without ECC to NAND-FLASH is not recommended
./bin/
Writing data without ECC to NAND-FLASH is not recommended
Writing data without ECC to NAND-FLASH is not recommended
…………………………………………………………………… 省略几千行
耐心等文件系统解压完后,使用/dev/mtdblock/3能正常启动,但只要读/写文件系统内容就会出现上述的警告,看着真是不爽。
上网查看说去掉这种警告有两种方法
1. 去掉警告信息(晕~掩耳盗铃@_@)。
2. 使用MTD的ecc校验,而不使用yaffs2的ecc校验(不知道到底为什么)。
在内核选项中去掉Lets Yaffs do its own ECC这个选项,果然成功了。
察看yaffs2源码
yaffs_mtdif.c有如下内容:
if (data && spare) {
if (dev->useNANDECC) { /* 配置了Lets Yaffs do its own ECC选项 */
retval =
mtd->read_ecc(mtd, addr, dev->nBytesPerChunk,
&dummy, data, spareAsBytes,
&yaffs_oobinfo);
} else {
retval =
mtd->read_ecc(mtd, addr, dev->nBytesPerChunk,
&dummy, data, spareAsBytes,
&yaffs_noeccinfo);
}
在mtd关于nand的驱动中nand_base.c有如下内容:
static int nand_read_ecc (struct mtd_info *mtd, loff_t from, size_t len,
size_t * retlen, u_char * buf, u_char * oob_buf, struct nand_oobinfo *oobsel)
{
/* use userspace supplied oobinfo, if zero */
if (oobsel == NULL) /* 当使用yaffs_noeccinfo时,条件语句为真 */
oobsel = &mtd->oobinfo;
return nand_do_read_ecc(mtd, from, len, retlen, buf, oob_buf, oobsel, 0xff);
}
在nand_do_read_ecc函数中有
eccmode = oobsel->useecc ? this->eccmode : NAND_ECC_NONE;
在使用yaffs2的ecc校验时在这句就把eccmode赋值为NAND_ECC_NONE(要不就不会出现警告),也就是说oobsel->useecc的值为0,真不知道对不对,使用yaffs2自己的ecc校验时怎么会把这个选项置为0呢?但是从实验看这个猜测应该是对的!
真不知道yaffs2的ECC校验和MTD的ECC校验之间到底是个什么样的关系!不知道大家有什么意见!
初装Debian
公司刚给配了个台式机,正好装个Linux学习学习,选来选去选中了Debian,据说是安全稳定,符合GNU且速度快,而且工具链很全,获得软件的方式简单(正好适合我这样的懒人)。由于没有光驱,所以想用硬盘安装,在网上又google了一番,终于找到了一篇傻瓜式安装教程《 》,虽然这篇文章写的已经很详细了,但我在安装时还是遇到了一些问题。
我安装的是debian 310a sarge版。公司给配的是方正的品牌机,买来时机器上就有windowxp和一个方正自己开发的“方正智能护理专家”的软件,可以备份/恢复系统,还在硬盘上创建了一个6G大小的分区,估计是放系统备份的吧。在装debian的时候需要在MBR中安装GRUB,一开始装到这里就继续不了了,说什么写入MBR失败,后来发现电脑在启动时先引导的是方正的护理软件,是不是把MBR锁住了不让修改呀,格式化掉那个6G的分区,卸载掉方正的那个软件,安装GRUB成功!到了网络检测,使用DHCP自动检测失败,那就稍后在配置吧。这样一步步安装,xwindow终于启动起来了,啊,怎么全是乱码,不过没关系,可以从网络安装字体,完了,刚才网络没配置成功选的稍后配置,而在genome下全是乱码,也不知道在那配置网络,没办法只好重启电脑,进入windows,下载字体文件ttf-arphic-gbsn00lp_2.11-6_all.deb和ttf-arphic-gkai00mp_2.11-6_all.deb重启进入Debian,安装字体,终于可以显示熟悉的中文了。
配置网络,输入IP地址、网关、DNS,提示网络不可达,怎么回事,仔细一看原来是IP地址和网关不在一个网段内,那windows怎么行呢,上网查找了许文章,也试了几种方法,还是不行,只能问网关本网段的网关地址了,输入后成功上网(唉,早知道这么简单也不用折腾了)。然后配置了数据源,一通apt-get,呵呵,终于可以用来工作/学习了:)
基于文档视图的NT服务程序
由于工作原因要求开发一个服务程序,需要有人机交互的界面,本想使用对话框模式来完成,但由于菜单、工具栏、分割窗口处理太麻烦,所以选择了文档视图结构,其中遇到了一些困难(其实说白了是对服务程序的理解不够详细)共享出来,以便大家再遇到这个问题时少走弯路。
先来简要说一下NT服务程序的主要几个环节:
1. 调用main方法来设置service_main方法。
2. 调用service_main方法设置ServiceCtrlHandler方法并启动工作循环(注意是工作循环)。
3. 利用ServiceCtrlHandler方法处理SCM(Service Control Manager)发给服务程序的消息。
第二步是使用工作循环,即代表着service_main方法不能返回直到服务停止,所以使用对话框实现服务程序时就自动符合了这个要求,因为DoMoudle函数直到对话框关闭后才返回,通常程序的处理流程都在对话框中处理,关闭对话框也就代表着要关闭服务程序,这时关闭对话框、DoMoudle函数返回、服务结束,很自然的一个流程就走了下来。但要使用文档视图结构上边这个流程就要做些变动了,因为文档视图结构程序初始化时是先创建文档模板,然后程序初始化完成并返回,所有工作都在MainFrame中完成,这样工作循环就没有建立起来,所以只能想一个变通的办法来实现工作循环,看下代码吧。
对话框模式:
void Service_start(DWORD dwArgc, LPTSTR *lpszArgv)
{
CxxxDlg dlg(&dlgParent);
theApp.m_pMainWnd = &dlg;
int nResponse = dlg.DoModal(); // 直到对话框关闭函数才返回
if (nResponse == IDOK)
{
}
else if (nResponse == IDCANCEL)
{
}
dlgParent.DestroyWindow();
}
文档视图模式:
void Service_start(DWORD dwArgc, LPTSTR *lpszArgv)
{
CSingleDocTemplate* pDocTemplate;
pDocTemplate = new CSingleDocTemplate(
IDR_MAINFRAME,
RUNTIME_CLASS(CPLMSPrefixServiceDoc),
RUNTIME_CLASS(CMainFrame),
RUNTIME_CLASS(CPLMSPrefixServiceView));
AddDocTemplate(pDocTemplate);
CCommandLineInfo cmdInfo;
ParseCommandLine(cmdInfo);
if (!ProcessShellCommand(cmdInfo))
return;
m_pMainWnd->ShowWindow(SW_SHOW);
m_pMainWnd->UpdateWindow();
// m_bIsRunning是服务程序控制开关,
// TRUE - 服务正在运行,
// FALSE - 服务程序关闭
// 如果服务程序在运行就一直执行循环(模拟工作循环,
// 但具体工作都在MainFrame中完成)
while (m_bIsRunning)
{
MSG msg;
while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
Sleep(50);
}
}
参考文章