1029|0

6943

帖子

19

资源

纯净的硅(中级)

DSP 2812: 使用C++实现的SCI主动站程序框架 [复制链接]

控制器与外界通讯,一般都会使用一些约定的通讯协议,或者使用类似于Linux控制台的字符终端交互。

大体上有两种通讯模式。一种是接收上位机之类的主机的命令,并执行和输出命令的执行结果或状态。在这种被动模式,开发板称为从动站;另外一种是定时发送命令给其他控制板或者仪表,读取或者控制其他设备。在这种主动模式,开发板被称为主动站。


我们创建了这两张模式下的程序框架。

这里介绍的是做为主动站的程序框架。


应用程序的编写

首先展示一下应用程序中是如何使用这个驱动的:


第一步初始化串口设备
[cpp] view plain copy
print?


  • scia().setBps(9600,board.clock());  



我们这里使用的SCIA,设置通讯速率是9600.其他参数使用默认值。

第二步定义Scia主站类对象,并设置收发缓冲区:
[cpp] view plain copy
print?


  • unsigned char sciRxBuf[256];  
  • unsigned char sciTxBuf[256];  
  •   
  • NDm::NApp::NF281x::CSciAMaster& sciaMaster = NDm::NApp::NF281x::CSciAMaster::ins();  
  • sciaMaster.setRxBuf(sciRxBuf,256);  
  • sciaMaster.setTxBuf(sciTxBuf,256);  
  •   
  • sciaMaster.reset();  



第三步在应用程序中实现报文处理的业务程序。

基本就是这样的循环:发送数据,然后接收数据,处理数据。

我的例子程序使用调试助手,接收DSP发来的start后,就发数据,然后DSP回复发送的数据,再发数据,再回复这样的循环。


[cpp] view plain copy
print?


  • unsigned char buf[10] = "start\n";  
  • int num = 6;  
  • sciaMaster.send(buf,num);  
  •   
  • while( true ){  
  •     num = sciaMaster.recv(buf,10);  
  •     if( num>0 ){  
  •         sciaMaster.send(buf,num);  
  •     }  
  • }  



驱动程序的设计

驱动程序时设计一个基类,然后为各串口实现子类。在子类中设定中断向量和相应的处理串口类。


基类的设计
[cpp] view plain copy
print?


  • namespace NF281x {  
  •   
  • /**
  • * SCI主模块驱动
  • * 由作为主动站的程序调用本类接口。
  • */  
  • class CSciMaster {  
  • public:  
  •     typedef TRingBuf CRingBuf;  
  •     typedef CRingBuf::CRingPos CRingPos;  
  • public:  
  •     CSciMaster( NDm::NHw::NF281x::CSci& sci  );  


[cpp] view plain copy
print?


  • private:  
  •     CRingBuf m_rx;  
  •     CRingPos m_rxPos;  
  •     CRingBuf m_tx;  
  •     CRingPos m_txPos;  
  •   
  •     NDm::NHw::NF281x::CSci& m_sci;  
  •   
  •     NDm::NApp::NF281x::CPieCtl& m_pie;  
  • };  
  •   
  • } /* namespace NF281x */  


这个类使用了两个循环缓冲区,用于缓存接收到的数据和要发送的数据。

定义了简单使用的接口函数


[cpp] view plain copy
print?


  • void reset();  
  • bool isError()const;  
  •   
  • inline NDm::NHw::NF281x::CSci& sci(){  
  •     return m_sci;  
  • }  
  •   
  • inline const NDm::NHw::NF281x::CSci& sci()const{  
  •     return m_sci;  
  • }  
  •   
  • inline void setTxBuf( unsigned char* buf,const unsigned int& size ){  
  •     m_tx.setBuf(buf,size);  
  • }  
  •   
  • inline void setRxBuf( unsigned char* buf,const unsigned int& size ){  
  •     m_rx.setBuf(buf,size);  
  • }  
  •   
  • inline bool ifTxEmpty()const{  
  •     return m_tx.getPos()==m_txPos;  
  • }  
  •   
  • inline bool ifRxEmpty()const{  
  •     return m_rx.getPos()==m_rxPos;  
  • }  
  •   
  • int send( const unsigned char* buf,const int& size );  
  •   
  • int recv( unsigned char* buf,const int& size );  


应用程序主要使用的函数就是send和recv函数。

因为驱动程序自带缓冲区,所以应用程序调用收发函数后,可以立即返回去处理其他业务,而不用等到串口数据发送完成。


[cpp] view plain copy
print?


  • /**
  • * 中断处理函数
  • * 这些函数应该在中断函数中被调用执行
  • * 用户程序应该执行PIE级别的ACK操作
  • */  
  • void irsRx();  
  • void irsTx();  
  •   
  • inline NDm::NHw::NF281x::CPie& pie(){  
  •     return m_pie;  
  • }  


真正的在SCI设备上收发数据在irsRx()和irsTx()中实现。这两个函数由中断调用。

这块可以详细看看收发函数是如何实现的。


[cpp] view plain copy
print?


  • void CSciMaster::irsRx(){  
  •     unsigned char buf[16];  
  •     unsigned int size;  
  •   
  •     if( m_sci.isInt_rxFf() ){  
  •         // 接收中断,将接收到的数据保存到缓存中  
  •         size = m_sci.rx(buf,16);  
  •         if( size>0 )  
  •             m_rx.push(buf,size);  
  •   
  •         m_sci.clrInt_rxFf();  
  •     }else{  
  •         reset();  
  •     }  
  • }  


可以看出来,接收中断中,只是将RXFIFO中的数据转移到接收的循环缓冲区中,并清除接收中断标志。

而对于发送的过程可能要复杂一些。

因为发送数据是由中断之外触发的。


[cpp] view plain copy
print?


  • int CSciMaster::send( const unsigned char* buf,const int& size ){  
  •     int rt = m_tx.getSize() - m_tx.getLen(m_txPos);  
  •     if( rt>size )  
  •         rt = size;  
  •   
  •     if( rt<=0 )  
  •         return 0;  
  •   
  •     m_tx.push(buf,rt);  
  •   
  •     m_sci.disInt_txFf();    // 禁用发送中断  
  •     startTx();  
  •   
  •     return rt;  
  • }  


发送函数将要发送的数据存入发送缓冲区中。在这个过程中,如果有发送中断到来,是不会影响send函数的正确性的,因为发送缓冲区,使用了两个pos对象,一个是写,一个是读。

但是真要发送数据时,是必须要禁止发送中断的。然后将缓冲区中的数据写到TXFIFO中,这个过程由startTx()函数实现。

为什么要有startTx()函数,因为这个过程在发送中断中也使用了。


[cpp] view plain copy
print?


  • void CSciMaster::irsTx(){  
  •     startTx();  
  •   
  •     // 如果队列为空,则停止中断  
  •     if( ifTxEmpty() )  
  •         m_sci.disInt_txFf();  
  • }  


startTx()函数做了什么?

[cpp] view plain copy
print?


  • void CSciMaster::startTx(){  
  •     unsigned char buf[16];  
  •     unsigned int size;  
  •   
  •     size = m_tx.getData(buf,16,m_txPos);  
  •     if( size>0 ){  
  •         size = m_sci.tx(buf,size);  
  •         m_txPos += size;  
  •     }  
  •   
  •     // 清除中断标志  
  •     m_sci.clrInt_txFf();  
  •   
  •     // 如果队列为空,则停止中断  
  •     if( ifTxEmpty() )  
  •         m_sci.disInt_txFf();  
  •     else  
  •         m_sci.enInt_txFf();  
  • }  


这样的话,我将recv()函数的实现也展示出来:

[cpp] view plain copy
print?


  • int CSciMaster::recv( unsigned char* buf,const int& size ){  
  •     int rt = m_rx.getLen(m_rxPos);  
  •     if( rt>size )  
  •         rt = size;  
  •     if( rt<=0 )  
  •         return 0;  
  •   
  •     m_rx.getDataAndMovePos(buf,rt,m_rxPos);  
  •     return rt;  
  • }  


这样的话,这个驱动就算是完整了。

其实这样的程序要考虑清楚各种并非事件之间的关系,成员变量是否会受影响,是否会影响程序的正确性,也不是一件容易的事情。

还好,这块的代码经过测试和使用,是没问题的。


其实Ti的实时操作系统SYSBIOS中的DEV模型也是非常好的框架,我在28335平台上就是实现了一个DEV的驱动。

子类的实现

以Scia为例


[cpp] view plain copy
print?


  • namespace NF281x {  
  •   
  • class CSciAMaster:public CSciMaster{  
  •     CSciAMaster();  
  • public:  
  •     static CSciAMaster& ins();  
  • };  


在其构造函数中需要对其进行特殊化处理

[cpp] view plain copy
print?


  • CSciAMaster::CSciAMaster():CSciMaster(NDm::NApp::NF281x::CSciaCtl::ins()){  
  •     CCpu::dint();  
  •   
  •     pie().setIrs_rxAInt(irsDm281xSciAMasterRx);  
  •     pie().setIrs_txAInt(irsDm281xSciAMasterTx);  
  •   
  •     CCpu::eint();  
  •     CCpu::ertm();  
  • }  
  •   
  • CSciAMaster& CSciAMaster::ins(){  
  •     static CSciAMaster i;  
  •     return i;  
  • }  


可以看出,还需要两个中断函数进行封装

[cpp] view plain copy
print?


  • extern "C" interrupt void irsDm281xSciAMasterRx(){  
  •     CSciAMaster& sci = CSciAMaster::ins();  
  •     sci.irsRx();  
  •   
  •     sci.pie().ack_rxAInt();  
  • }  
  •   
  • extern "C" interrupt void irsDm281xSciAMasterTx(){  
  •     CSciAMaster& sci = CSciAMaster::ins();  
  •     sci.irsTx();  
  •   
  •     sci.pie().ack_txAInt();  
  • }  


中断函数的处理也比较简单,只需要将执行权传递给相应的中断处理成员函数即可。

回复
您需要登录后才可以回帖 登录 | 注册

查找数据手册?

EEWorld Datasheet 技术支持

最新文章 更多>>
    关闭
    站长推荐上一条 1/10 下一条

    About Us 关于我们 客户服务 联系方式 器件索引 网站地图 最新更新 手机版

    站点相关: 安防电子 汽车电子 手机便携 工业控制 家用电子 医疗电子 测试测量 网络通信 物联网

    北京市海淀区知春路23号集成电路设计园量子银座1305 电话:(010)82350740 邮编:100191

    电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2022 EEWORLD.com.cn, Inc. All rights reserved
    快速回复 返回顶部 返回列表