2016-03-31 7 views
0

私は、USB to sealコンバータ(BUB)を介してModernDevice.comからコンピュータにATMega328p(MCU)を接続しました。 MCUは、TX、RX、およびGNDラインによってBUBに接続されています。ATMega328p:USARTを介してバイトを送信する奇妙な動作

UART.h:

インターネット上で発見され、私のニーズに適合させることができる多くのソリューションに基づいて書かれたU(S)ARTを使用してデータを送信するために

、私はこのライブラリを使用して、

#ifndef __UART_H_ 
#define __UART_H_ 

#include <string.h> 

#include "AVRBase.h" 

#define BAUD 38400 
#include <util/setbaud.h> 

#define SERIAL_QUEUE_SIZE 10 

struct RingBuffer { 
    unsigned char achBuffer[SERIAL_BUFFER_SIZE]; 
    int nHead; 
    int nTail; 
}; 

class UART : public AVRBase { 
    public: 
    UART(); 
    ~UART(); 

    void Init(); 
    void Stop(); 

    void StoreRxChar(uint8_t); 
    void ReceiveNextByte(); 
    void SendNextByte(); 

    int Available(); 
    int Read(); 
    bool StringIsComplete(); 
    String GetInputString(); 

    void Flush(); 
    size_t Write(char); 
    size_t SendStr(String); 

    private: 
    RingBuffer m_sRxBuffer; 
    RingBuffer m_sTxBuffer; 

    String m_strInputString; 
}; 


#endif 

UART.cpp:

#include "UART.h" 

UART::UART() { 
    memset(m_sRxBuffer.achBuffer, 0, SERIAL_BUFFER_SIZE); 
    m_sRxBuffer.nHead = 0; 
    m_sRxBuffer.nTail = 0; 
    memset(m_sTxBuffer.achBuffer, 0, SERIAL_BUFFER_SIZE); 
    m_sRxBuffer.nHead = 0; 
    m_sRxBuffer.nTail = 0; 
} 

UART::~UART() { 

} 

void UART::Init() { 
    cbi(SMCR, PRUSART0); 

    // Assyncronous USART 
    cbi(UCSR0C, UMSEL01); 
    cbi(UCSR0C, UMSEL00); 

    // No parity 
    cbi(UCSR0C, UPM01); 
    cbi(UCSR0C, UPM00); 

    // 2 stop bits 
    sbi(UCSR0C, USBS0); 

    // 8 data bits 
    cbi(UCSR0C, UCSZ02); 
    sbi(UCSR0C, UCSZ01); 
    sbi(UCSR0C, UCSZ00); 

    // Clock polarity. Set to zero in assync mode 
    cbi(UCSR0C, UCPOL0); 

    UBRR0H = UBRRH_VALUE; 
    UBRR0L = UBRRL_VALUE; 

    // no 2x speed 
    cbi(UCSR0A, U2X0); 

    sbi(UCSR0B, RXEN0); 
    sbi(UCSR0B, TXEN0); 

    // Enable receive complete interrupt 
    sbi(UCSR0B, RXCIE0); 

    // Disable transmit complete interrupt 
    cbi(UCSR0B, TXCIE0); 

    // Disable date register empty interrupt 
    cbi(UCSR0B, UDRIE0); 
} 

void UART::Stop() { 
    while (m_sTxBuffer.nHead != m_sTxBuffer.nTail); 

    cbi(UCSR0B, RXEN0); 
    cbi(UCSR0B, TXEN0); 
    cbi(UCSR0B, RXCIE0); 
    cbi(UCSR0B, UDRIE0); 

    m_sRxBuffer.nHead = m_sRxBuffer.nTail; 
} 

void UART::StoreRxChar(uint8_t nC) { 
    int nI = (unsigned int) (m_sRxBuffer.nHead + 1) % SERIAL_BUFFER_SIZE; 

    if (nI != m_sRxBuffer.nTail) { 
    m_sRxBuffer.achBuffer[m_sRxBuffer.nHead] = nC; 
    m_sRxBuffer.nHead = nI; 
    } 
} 

void UART::ReceiveNextByte() { 
    unsigned char chC = UDR0; 
    StoreRxChar(chC); 
} 

void UART::SendNextByte() { 
    if (m_sTxBuffer.nHead == m_sTxBuffer.nTail) { 
    //Buffer empty, so disable interrupts 
    cbi(UCSR0B, UDRIE0); 
    } 
    else { 
    unsigned char chC = m_sTxBuffer.achBuffer[m_sTxBuffer.nTail]; 
    m_sTxBuffer.nTail = (m_sTxBuffer.nTail + 1) % SERIAL_BUFFER_SIZE; 
    UDR0 = chC; 
    } 
} 

int UART::Available() { 
    return (unsigned int) (SERIAL_BUFFER_SIZE + m_sRxBuffer.nHead - m_sRxBuffer.nTail) % SERIAL_BUFFER_SIZE; 
} 

int UART::Read() { 
    // if the head isn't ahead of the tail, we don't have any characters 
    if (m_sRxBuffer.nHead == m_sRxBuffer.nTail) { 
    return -1; 
    } 
    else { 
    unsigned char chC = m_sRxBuffer.achBuffer[m_sRxBuffer.nTail]; 

    m_sRxBuffer.nTail = (unsigned int)(m_sRxBuffer.nTail + 1) % SERIAL_BUFFER_SIZE; 
    return chC; 
    } 
} 

bool UART::StringIsComplete() { 

    while (Available()) { 
    // get the new byte: 
    char cInChar = (char)Read(); 
    // add it to the inputString: 
    m_strInputString += cInChar; 
    // if the incoming character is a newline, set a flag 
    // so the main loop can do something about it: 
    if (cInChar == '\n') { 
     return true; 
    } 
    } 
    return false; 
} 

String UART::GetInputString() { 
    String strTransfer = m_strInputString; 
    m_strInputString = ""; 
    return strTransfer; 
} 

void UART::Flush() { 
    while (m_sTxBuffer.nHead != m_sTxBuffer.nTail); 
} 

size_t UART::Write(char chC) { 
    int nI = (m_sTxBuffer.nHead + 1) % SERIAL_BUFFER_SIZE; 

    // If the output buffer is full, there's nothing for it other than to 
    // wait for the interrupt handler to empty it a bit 
    // ???: return 0 here instead? 
    while (nI == m_sTxBuffer.nTail); 

    m_sTxBuffer.achBuffer[m_sTxBuffer.nHead] = chC; 
    m_sTxBuffer.nHead = nI; 

    sbi(UCSR0B, UDRIE0); 

    return 1; 
} 

size_t UART::SendStr(String strMessage) { 
    size_t nSize = 0; 
    for (uint8_t nI = 0; nI < strMessage.length(); nI++) { 
    nSize += Write(strMessage[nI]); 
    } 
    return nSize; 
} 

以下のクラスは、UARTの制御を取る:

COM.h:

#ifndef __COM_H_ 
#define __COM_H_ 

#include "AVRBase.h" 
#include "UART.h" 

class COM : public AVRBase { 
    public: 
    COM(); 
    ~COM(); 

    void Run(); 

    void SendNextByteWrapper(); 
    void ReceiveNextByteWrapper(); 

    void SetUserCallback(void (*)(String)); 
    void Echo(String); 

    private: 
    UART m_cUART; 

    void (* m_pRunUserCallback) (String); 

    bool m_bLed; 
    bool m_bUserCallbackWasSet; 
}; 

#endif 

COM.cpp:

COMModule.h:メインコードで

#include "COM.h" 

COM::COM() { 
    m_bUserCallbackWasSet = false; 
} 

COM::~COM() { 
} 

void COM::Run() { 

    PAD3_IS_OUTPUT; 
    m_bLed = true; 

    m_cUART.Init(); 

    while(1) { 

    if (m_bLed) { 
     PAD3_HIGH; 
     m_bLed = false; 
    } 
    else { 
     PAD3_LOW; 
     m_bLed = true; 
    } 

    Delay_ms(100); 
    } 
} 

void COM::SendNextByteWrapper() { 
    m_cUART.SendNextByte(); 
} 

void COM::ReceiveNextByteWrapper() { 
    m_cUART.ReceiveNextByte(); 
    if (m_cUART.StringIsComplete()) { 
    if (m_bUserCallbackWasSet) { 
     m_pRunUserCallback(m_cUART.GetInputString()); 
    } 
    } 
} 

void COM::SetUserCallback(void (* pRunUserCallback) (String)) { 
    if (pRunUserCallback != NULL) { 
    m_pRunUserCallback = pRunUserCallback; 
    m_bUserCallbackWasSet = true; 
    } 
} 

void COM::Echo(String strReceived) { 

    // TEST 1 
    m_cUART.SendStr(strReceived); // Sends only trash! 

    // TEST 2 
    m_cUART.Write('S'); 
    m_cUART.Write('U'); 
    m_cUART.Write('C'); 
    m_cUART.Write('C'); 
    m_cUART.Write('E'); 
    m_cUART.Write('S'); 
    m_cUART.Write('S'); 
    m_cUART.Write('\n'); // Sends SUCCESS 

    // TEST 3 
    char Test[] = "BLERG\n"; 

    for (uint8_t nI = 0; nI <= 5; nI++) { 
    m_cUART.Write(Test[nI]); 
    } 
    // Sends only trash! 


    // TEST 4 
    for (uint8_t nI = 0; nI <= 5; nI++) { 
    m_cUART.Write(Test[0]); 
    } 
    // Sends BBBBB 
} 

は、以下のコードは、割り込みハンドラを実装

#include "COM.h" 

COM cCOM; 

void ManageUART(String); 

SIGNAL(USART_RX_vect) { 
    cCOM.ReceiveNextByteWrapper(); 
} 

ISR(USART_UDRE_vect) { 
    cCOM.SendNextByteWrapper(); 
} 

int main(void) { 
    sei(); 
    cCOM.SetUserCallback(&ManageUART); 
    cCOM.Run(); 
} 

void ManageUART(String strInput) { 
    cCOM.Echo(strInput); 
} 

だから、このすべての紹介の後、問題は、私がMCUに接続し、バイトを送信するとき、私はm y端子にテスト2とテスト4が実行されたとき(COM.cppのメソッドEcho)。配列の文字を送信するために、char配列またはループ内の文字列配列にアクセスするたびに、デスクトップ端末にゴミ箱が表示されます。

あなたは何が間違っているのか推測していますか?


編集:ここでは別の手がかりは、次のとおりです。

void COM::Echo(String strReceived) { 

    char Test[] = "BLERG\n"; 

    m_cUART.Write(Test[0]); 
    Delay_ms(100); 
    m_cUART.Write(Test[1]); 
    m_cUART.Write(Test[2]); 
    m_cUART.Write(Test[3]); 
    m_cUART.Write(Test[4]); 
    m_cUART.Write(Test[5]); 

    // The above code works. The terminal receives BLERG. 

    uint8_t nI=0; 
    uint8_t nA=3; 
    while (nI <= 5) { 
    m_cUART.Write(Test[nA]); 
    nI++; 
    } 
    // The above code works too... The terminal receives RRRRRR. 

    for (uint8_t nJ = 0; nJ <= 5; nJ++) { 
    m_cUART.Write(Test[nJ]); 
    //Delay_ms(1); 
    } 
    // The above code sends only trash: ÿÿÿÿÿÿ 
} 

EDIT2:もう一つの事実。私は実際になぜこれが理解できないのですか:

void COM::Echo(String strReceived) { 

    char Test[] = "BLERG\n"; 

    m_cUART.Write(Test[0]); 
    Delay_ms(100); 
    m_cUART.Write(Test[1]); 
    m_cUART.Write(Test[2]); 
    m_cUART.Write(Test[3]); 
    m_cUART.Write(Test[4]); 
    m_cUART.Write(Test[5]); 

    // The above code works. The terminal receives BLERG. 

    uint8_t nI=0; 
    uint8_t nA=3; 
    while (nI <= 5) { 
    m_cUART.Write(Test[nA]); 
    nI++; 
    } 
    // The above code works too... The terminal receives RRRRRR. 

nA=0; 
m_cUART.Write(Test[nA]); // MCU LOCKS HERE! 

    for (uint8_t nJ = 0; nJ <= 5; nJ++) { 
    m_cUART.Write(Test[nJ]); 
    //Delay_ms(1); 
    } 
    // The above code sends only trash: ÿÿÿÿÿÿ 
} 
+0

私はあなたを正しく理解していますか?ATMegaから送信されたときにデスクトップ上に正しくメッセージが表示されますが、UARTバッファにアクセスすると他の時間はゴミ箱に入りますか? – pingul

+0

テストごとに 'UART :: Flush()'を呼び出すべきでしょう。私はそれがあなたの問題を解決するとは思わない(したがって、答えの代わりにコメント)が、他の問題を隠す可能性がある。 – TriskalJM

+0

@pingul、それはそれより悪いです。 UARTバッファを介して私はゴミ箱に入ります。変数としてインデックスを使用するchar配列を介して、私はごみを取得します。しかし、インデックスとして定数を使用して、char配列を使用して、私は正しい出力を得る。 – ENHering

答えて

1

コードに競合状態があるようです。フィールドm_sTxBuffer.achBufferとm_sTxBuffer.nHeadは、メインスレッドとUSART_UDRE_vect割り込みハンドラの両方からアクセスされます。

これを処理する方法はいくつかあります。すべての割り込み(sei/cli)をグローバルに無効にし、nHeadおよびachBufferフィールドをvolatileとしてマークし、compiler memory barriers to prevent reorderingを使用して独自のソリューションを展開できます。

またはatomic.h from avr-libcを使用できます。