DSP

DSP 2812: 使用C++封装SCI

2019-07-13 18:17发布

使用SCI模块进行通信,是装置与外界的主要方式。比如与上位机通信,接入GPRS模块,接入485电度表,或者板间通信等等。 SCI的控制应该说还是比较简单的,但是也是非常灵活的。支持FIFO模式,支持测试模式,支持中断模式,而且还有各种通信参数的设置等。 DSP2812有两个SCI模块。其实DSP28335这样的片子的很多外设和2812是一样使用的,程序都是可以通用的。这里我们只对2812进行封装。
先定义一个基类:CSci,然后为SCI-A和SCI-B分别定义子类CScia,CScib.
基类定义如下: namespace NF281x{ /** * SCI基类 */ class CSci{ public: CSci( volatile unsigned int& ccr, volatile unsigned int& ctl1, volatile unsigned int& ctl2, volatile unsigned int& lbaud, volatile unsigned int& hbaud, volatile unsigned int& rxst, volatile unsigned int& rxbuf, volatile unsigned int& txbuf, volatile unsigned int& fftx, volatile unsigned int& ffrx, volatile unsigned int& ffct); private: volatile unsigned int& m_ccr; volatile unsigned int& m_ctl1; volatile unsigned int& m_ctl2; volatile unsigned int& m_lbaud; volatile unsigned int& m_hbaud; volatile unsigned int& m_rxst; volatile unsigned int& m_rxbuf; volatile unsigned int& m_txbuf; volatile unsigned int& m_fftx; volatile unsigned int& m_ffrx; volatile unsigned int& m_ffct; };

一看基类的构造函数这么复杂,其实没关系,应用程序使用时没有这些的。 应用程序时不会直接使用基类的构造函数的,这在我们定义子类的时候传入相应的寄存器即可。 其实也说明了一点,SCI的使用还是有点复杂,不说难,也是容易出错的。所以,封装成简洁的接口是很有必要的。
看看子类的接口: namespace NF281x{ class CScia:public CSci{ public: CScia(); }; }
namespace NF281x{ class CScib:public CSci{ public: CScib(); }; } 也就是说应用程序使用SCI-B对象时,只需要定义一个CScib的对象,而且构造时不需要任何参数。
接下来是通讯参数的相关接口 /** * 获取停止位数 * @return * - 0x00 1bit停止位 * - 0x80 2bits停止位 */ inline unsigned int getStop()const{ return NDm::getBit(m_ccr); } /** * 是否是1位停止位 * @return */ inline bool isStop_1()const{ return getStop()==0; } /** * 是否是2位停止位 * @return */ inline bool isStop_2()const{ return getStop()!=0; } /** * 设置停止位 * @param st * @see getStop() */ inline void setStop( const unsigned int& st ){ if( st==0 ) setStop_1(); else setStop_2(); } /** * 设置1位停止位 */ inline void setStop_1(){ NDm::bitClr(m_ccr); } /** * 设置2位停止位 */ inline void setStop_2(){ NDm::bitSet(m_ccr); } /** * 获取校验方式 * @return * - 0x40: 奇校验 * - 0x60: 偶校验 * - 其他: 无校验 */ inline unsigned int getParity()const{ return m_ccr & 0x60; // bit 6-5 } /** * 是否奇校验 * @return */ inline bool isParity_odd()const{ return getParity()==0x40; } /** * 是否偶校验 * @return */ inline bool isParity_even()const{ return getParity()==0x60; } /** * 是否无校验 * @return */ inline bool isParity_none()const{ return (getParity())&0x20==0; } /** * 设置校验方式 * @param p * @see getParity(); */ inline void setParity( const unsigned int& p ){ if( p==0x60 ) setParity_even(); else if( p==0x40 ) setParity_odd(); else setParity_none(); } /** * 设置无校验 */ inline void setParity_none(){ NDm::bitClr(m_ccr); } /** * 设置为偶校验 */ inline void setParity_even(){ m_ccr |= 0x60; } /** * 设置为奇校验 */ inline void setParity_odd(){ m_ccr = (m_ccr&0xDF) | 0x20; } /** * 获取字符长度 * @return 0~7:1~8 */ inline unsigned int getChar()const{ return m_ccr&0x07; // bit 0~3 } /** * 设置字符长度 * @param len * @see getChar(); */ inline void setChar( const unsigned int& len ){ m_ccr = (m_ccr&0xF8) | (len&0x07); } /**************** 通信参数 **************/ inline unsigned long getBps( const CClocking& clk )const{ return clk.getClk_sci()/(( getBrr()+1 ) * 8); } inline void setBps( const unsigned long& bps,const CClocking& clk ){ setBrr(clk.getClk_sci()/(8*bps) -1 ); } void init( const unsigned int& ch=8,const unsigned int& stop=1,const unsigned int& parity=0 ); 定义丰富的接口查看运行状态
/** * 发送缓冲区可写 * @return */ inline bool isTxRdy()const{ return NDm::isBitSet(m_ctl2); } /** * 发送线路空闲 * @return */ inline bool isTxEmpty()const{ return NDm::isBitSet(m_ctl2); } /** * 接收缓冲区可读 * @return */ inline bool isRxRdy()const{ return NDm::isBitSet(m_rxst); }
定义操作相关接口 inline bool isTxEn()const{ return NDm::isBitSet(m_ctl1); } inline void enTx( const bool& en ){ if( en ) enTx(); else disTx(); } inline void enTx(){ NDm::bitSet(m_ctl1); } inline void disTx(){ NDm::bitClr(m_ctl1); } inline bool isRxEn()const{ return NDm::isBitSet(m_ctl1); } inline void enRx( const bool& en ){ if( en ) enRx(); else disRx(); } inline void enRx(){ NDm::bitSet(m_ctl1); } inline void disRx(){ NDm::bitClr(m_ctl1); } inline void tx( const unsigned char& c ){ m_txbuf = c; } inline void rx( unsigned char& c )const{ c = m_rxbuf&0xFF; } /** * 复位模块状态机 * 该操作不会修改参数 * @param delay 处于复位时常 */ void reset( const unsigned int& delay=100 );
定义错误检测的接口 /** * 接收错误 * 这是一个总的接收错误标志。 * 一般程序测试错误,都应该测试该标志 * 当有错误标志时,必须使用reset来进行复位。 * @return */ inline bool isRxErr()const{ return NDm::isBitSet(m_rxst); } /** * 接收信号至少有10bit的低电平 * @return */ inline bool isRxErr_break()const{ return NDm::isBitSet(m_rxst); } /** * 没有收到正确的停止位 * @return */ inline bool isRxErr_frame()const{ return NDm::isBitSet(m_rxst); } /** * 校验没通过 * @return */ inline bool isRxErr_parity()const{ return NDm::isBitSet(m_rxst); } /** * 程序或者DMA没有及时取走接收到的数据 * @return */ inline bool isRxErr_overrun()const{ return NDm::isBitSet(m_rxst); } /** * FIFO顶层数据有帧错误 * @note 只在FIFO模式下有效 * @return */ inline bool isFFErr_frame()const{ return NDm::isBitSet(m_rxbuf); } /** * FIFO顶层数据有校验错误 * @note 只在FIFO模式下有效 * @return */ inline bool isFFErr_parity()const{ return NDm::isBitSet(m_rxbuf); }
定义中断相关的接口
/** * 接收错误时产生中断 * @return */ inline bool isIntEn_rxErr()const{ return NDm::isBitSet(m_ctl1); } inline void enInt_rxErr( const bool& en ){ if( en ) enInt_rxErr(); else disInt_rxErr(); } inline void enInt_rxErr(){ NDm::bitSet(m_ctl1); } inline void disInt_rxErr(){ NDm::bitClr(m_ctl1); } /** * 可以读取接收缓冲区或者检测到Break时产生中断 * @return */ inline bool isIntEn_rxRdyOrBreak()const{ return NDm::isBitSet(m_ctl2); } inline void enInt_rxRdyOrBreak( const bool& en ){ if( en ) enInt_rxRdyOrBreak(); else disInt_rxRdyOrBreak(); } inline void enInt_rxRdyOrBreak(){ NDm::bitSet(m_ctl2); } inline void disInt_rxRdyOrBreak(){ NDm::bitClr(m_ctl2); } /** * 可以写数据到发送缓冲区时产生中断 * @return */ inline bool isIntEn_txRdy()const{ return NDm::isBitSet(m_ctl2); } inline void enInt_txRdy( const bool& en ){ if( en ) enInt_txRdy(); else disInt_txRdy(); } inline void enInt_txRdy(){ NDm::bitSet(m_ctl2); } inline void disInt_txRdy(){ NDm::bitClr(m_ctl2); }
定义FIFO相关的接口 inline bool isEnFifo()const{ return NDm::isBitSet(m_fftx); } inline void enFifo(){ NDm::bitSet(m_fftx); } /** * 复位 * 复位FIFO与SCI模块 * @param wait */ void resetFf( const unsigned int& wait=1000 ); /** * 复位发送队列 * @param wait */ void resetTxFf( const unsigned int& wait=100 ); /** * 复位接收队列 * @param wait */ void resetRxFf( const unsigned int& wait=100 ); /** * 发送字符延时 * @return */ inline unsigned int getFfTxDelay()const{ return m_ffct & 0x00FF; } inline void setFfTxDelay( const unsigned int& delay=0 ){ m_ffct = (m_ffct&0xFF00)| (delay&0x00FF); } int tx( const unsigned char* buf,const int& size ); int rx( unsigned char* buf,const int& size )const; /** * 发送队列数据个数 * @return */ inline unsigned int txFfState()const{ return (m_fftx&0x1F00)>>8; } /** * 接收队列数据个数 * @return */ inline unsigned int rxFfState()const{ return (m_ffrx&0x1F00)>>8; } /** * 接收队列溢出 * @return */ inline bool isRxFfOverFlow()const{ return NDm::isBitSet(m_ffrx); } /** * 清除接收队列溢出标志 */ inline void clrRxFfOverFlow(){ NDm::bitSet(m_ffrx); } /** * 使能发送队列中断 */ inline void enInt_txFf(){ NDm::bitSet(m_fftx); } /** * 使能接收队列中断 */ inline void enInt_rxFf(){ NDm::bitSet(m_ffrx); } /** * 禁用发送队列中断 */ inline void disInt_txFf(){ NDm::bitClr(m_fftx); } /** * 禁用接收队列中断 */ inline void disInt_rxFf(){ NDm::bitClr(m_ffrx); } /** * 发送队列中断是否使能 * @return */ inline bool isIntEn_txFf()const{ return NDm::isBitSet(m_fftx); } /** * 接收队列中断是否使能 * @return */ inline bool isIntEn_rxFf()const{ return NDm::isBitSet(m_ffrx); } /** * 是否有发送队列中断 * @return */ inline bool isInt_txFf()const{ return NDm::isBitSet(m_fftx); } /** * 是否有接收队列中断 * @return */ inline bool isInt_rxFf()const{ return NDm::isBitSet(m_ffrx); } /** * 清除发送队列中断 */ inline void clrInt_txFf(){ NDm::bitSet(m_fftx); } /** * 清除接收队列中断 */ inline void clrInt_rxFf(){ NDm::bitSet(m_ffrx); } /** * 获取发送队列中断级别 * @return */ inline unsigned int getTxFfIntLevel()const{ return m_fftx & 0x001F; } /** * 获取接收队列中断级别 * @return */ inline unsigned int getRxFfIntLevel()const{ return m_ffrx & 0x001F; } /** * 设置发送队列中断级别 * @param level */ inline void setTxFfIntLevel( const unsigned int& level=0 )const{ m_fftx = ((m_fftx&0xFFE0)|(level&0x001F)); } /** * 设置发送队列中断级别 * @param level */ inline void setRxFfIntLevel( const unsigned int& level=16 )const{ m_ffrx = ((m_ffrx&0xFFE0)|(level&0x001F)); }
这里我还定义了一个为将来扩展使用的函数,用于一般性的串口使用初始化过程函数 /** * 默认的初始化参数 * 这个函数给后续简单使用做封装 * 默认设置使用FIFO模式,但是参数不对通信速率初始化 */ void defaultInit();
也就是说,面对SCI这么灵活的使用方法,这么多接口,应用程序使用起来还是比较麻烦的。将来要抽象出SCI的普遍用法,或者说几种比较实用的用法。