使用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的普遍用法,或者说几种比较实用的用法。