2016-03-31 25 views
0

我有一個ATMega328p(MCU)通過USB連接到我的電腦密封轉換器(BUB)從ModernDevice.com。 MCU通過TX,RX和GND線連接到BUB。ATMega328p:奇怪的行爲通過USART發送字節

要發送使用U(S)ART數據,我使用這個庫,筆試基礎上,可以在互聯網上找到很多解決方案,並適用於我的需求:

UART.h:

#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:

#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 
} 

在主代碼,下面的代碼實現中斷處理程序:

COMModule.h:

#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上得到一些ASCII的答案當TEST 2和TEST 4執行時(方法Echo,在COM.cpp上),執行y終端。每當我訪問循環中的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字段標記爲易失性,並使用compiler memory barriers to prevent reordering。您可以使用atomic.h from avr-libc