簡體   English   中英

ATMega328p:奇怪的行為通過USART發送字節

[英]ATMega328p: Strange behaviour sending bytes through USART

我有一個ATMega328p(MCU)通過ModernDevice.com的USB到密封轉換器(BUB)連接到我的計算機。 MCU通過TX,RX和GND線連接到BUB。

為了使用U(S)ART發送數據,我使用了該庫,該庫是基於可以在Internet上找到的許多解決方案編寫的,並且可以滿足我的需求:

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並發送一個字節時,只有在執行TEST 2和TEST 4時,我才能在終端上得到一些ASCII答案(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: ÿÿÿÿÿÿ
}

您的代碼中似乎包含競爭條件。 可以從主線程和從USART_UDRE_vect中斷處理程序中訪問字段m_sTxBuffer.achBuffer和m_sTxBuffer.nHead。

有幾種方法可以解決此問題。 您可以推出自己的解決方案:全局禁用所有中斷(sei / cli),將nHead和achBuffer字段標記為volatile,並使用編譯器內存屏障來防止重新排序

或者,您可以使用avr-libc中的atomic.h

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM