ATMega328p:通过USART发送字节的奇怪行为
ATMega328p: Strange behaviour sending bytes through USART
我有一个ATMega328p(MCU)通过ModernDevice.com的USB到密封转换器(BUB)连接到我的计算机。MCU通过TX、RX和GND线连接到BUB。
为了使用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[] = "BLERGn";
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答案(方法Echo,在COM.cpp上)。每次我访问循环中的char数组或字符串数组来发送数组字符时,我都会在桌面终端中得到垃圾。
你猜出什么地方可能出了问题吗?
编辑:这是另一条线索:
void COM::Echo(String strReceived) {
char Test[] = "BLERGn";
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: ÿÿÿÿÿÿ
}
第二版:还有一个事实。我真的不明白为什么:
void COM::Echo(String strReceived) {
char Test[] = "BLERGn";
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: ÿÿÿÿÿÿ
}
您的代码中似乎存在竞争条件。字段m_sTxBuffer.achBuffer和m_sTxBuffer.nHead可从主线程和USART_UDRE_ect中断处理程序访问。
有几种方法可以解决这个问题。您可以推出自己的解决方案:全局禁用所有中断(sei/cli),将nHead和achBuffer字段标记为volatile,并使用编译器内存屏障来防止重新排序。
或者,您可以使用avr-libc中的atomic.h。
相关文章:
- 是否通过向封闭对象的地址添加字节偏移量来访问子对象
- 通过浮点指针操作字节向量
- 如何通过组合红色和蓝色无符号字节来优化混合?
- 通过QDataStream从客户端发送到服务器的字节不匹配
- 如何在C++中将整数转换为字节,以便 Unity 在通过 UDP 传输后能够理解它们
- 我正在尝试修改一个字节数组并通过 JNI 将其从 c++ 发送到 java
- 如何在带有 mbed 的 nucleo f446re 之间通过带有 UARTSerial 类的 USB 发送字节数组?
- 如何通过1024字节缓冲区和强力冲洗在插座连接上发送6个字符的C弦
- 如何通过c 中的void*的字节迭代
- 通过索引将字节插入mutli字节类型
- 如何通过libpq c api插入字节的数组
- 如何分别通过 QSslSocket 发送两个字节
- 正确处理字节对齐问题 - 通过UDP在16位嵌入式系统和32位桌面之间
- 有没有办法通过共享内存中的指针到达某个字节
- 通过 void* 和 reinterpret_cast从字节字段读取
- 录制音频,存储在缓冲区中,然后通过PulseAudio将字节写入声卡
- 在c++中通过串行发送一个带符号的整数作为字节,Arduino读取它
- 通过openssl/c/c++以字节形式读取RSA公钥
- ATMega328p:通过USART发送字节的奇怪行为
- 将额外的字节填充到要通过网络发送的flatbuffer的缓冲区指针