The Universal Asynchronous Receiver/Transmitter, commonly referred to as the UART, is an asynchronous transceiver that is part of the computer hardware. It converts the data to be transferred between serial communication and parallel communication. As a chip that converts parallel input signals into serial output signals, the UART is typically integrated into the connections of other communication interfaces.
The actual physical representation is a stand-alone modular chip or as a peripheral device integrated in the microprocessor. It is generally RS-232C-compliant and is paired with a standard signal amplitude conversion chip such as Maxim's MAX232 as an interface to external devices. A product that adds a synchronous sequence signal conversion circuit to the UART is called a USART (Universal Synchronous Asynchronous Receiver Transmitter).
The UART is a general purpose serial data bus for asynchronous communication. The bus bidirectional communication enables full duplex transmission and reception. In embedded design, the UART is used for communication between the host and the auxiliary device, such as communication between the car audio and the external AP, and communication with the PC includes communication with the monitor debugger and other devices such as EEPROM.
The UART receives data, one byte and one byte is received. The underlying hardware can only know that it has received a byte and then save it in a buffer. How do you judge that the current one-frame agreement is over? In other words, I want to send a protocol frame. According to the agreement, he is not fixed. How do you judge that it is now closed?
method one:
Some people may think that I am changing the judgment, that is, in the receiving driver function, to resolve the protocol, generally this way:
#pragma vector = INTSR2_vect
__interrupt staTIc void r_uart2_interrupt_receive(void)
{
Buffer[CNT] = RXD2;
CNT++;
If(CNT 》 frame header length){
Find the frame header and remember the frame header position;
}
/ / Find the frame length position
//Wait for receiving
/ / Determine the end of the frame or check
/ / Notification APP, one frame received
//Please sign
SRIF2 = 0;
}
In doing so, I feel that it is the most efficient. When I drive the driver layer, I have to expose __interrupt staTIc void r_uart2_interrupt_receive(void) to the user, and provide the API such as the user's underlying receiving flag.
Method Two:
Some people will think that adding a byte length before the protocol is not finished, according to this area to receive, and then received, tell the APP layer or the protocol parsing layer. The premise of this method is that everyone will do this according to this, otherwise the communication will fail and it is suitable for internal use.
Method three:
I started using the TIMER_OUT method. What do you mean? For example, at 9600 baud rate, it takes about 1ms to send a byte. If you think that the transmission is continuous rather than intermittent, then can I think that you are more than 1MS time (in order to leave enough wealth, think 20MS ) If no interrupt occurs, I can assume that the reception is complete. The APP or protocol parsing module is then notified to resolve the protocol. But there are a few issues to be aware of when dealing with this:
1. If the other party uses the query method to send, it needs to obtain the maximum interrupt processing time of the other party;
2. The transmission interval between frames must be greater than the TIMER_OUT time set by the receiver; (In fact, if this is not satisfied, it can also be processed. In the case that the RAM resources are sufficient, the protocol parsing module goes according to the idea of ​​parsing multiple frames. write)
3, the entire communication bandwidth is pulled down, because the frame interval is TIMER_OUT time, certainly above the MS level, generally 20~50ms;
But this method is the simplest and also suitable for decoupling, which is convenient for modular packaging.
Method four:
The method I am using now is a circular buffer. That is, the UART has been receiving data and put it in a ring buffer (to prevent overflow), the APP or protocol parsing module keeps reading data and doing protocol parsing. The advantage of this method is that the processing speed is fast and there is no TIMER_OUT time. The problem is that I don't know how to decouple the buffer. I simply threw the buffer to the protocol parsing module.
Since I talked about the UART driver package, I will talk about my current practice. In fact, I just learned to package this piece. The level is limited. I just want to talk to you.
First I defined several interfaces:
//UART0
Extern void uart0_drive_mode_init(void (*pRxCallBack)(uint8_t));
Extern bool uart0_cfg(uart_cfg_t *ptUartCfg,uint32_t wBaudRate,uint16_t hwErrorRange,void (*callback)());
Extern bool uart0_txd_query(uint8_t *pchTxdBuffer,uint16_t hwTxdNum);
Extern void uart0_enable_rx_interrupt(interrupt_level_t tLevel);
Extern void uart0_disable_rx_interrupt(void);
Extern void uart0_set_tx_interrupt_level(interrupt_level_t tLevel);
Extern bool uart0_txd_interrupt_start(uint8_t *pchBuffer,uint16_t hwTxdLong);
/**************************** UART0 ******************** ********/
/ / Module initialization
#define USER_UART0_DRIVE_MODE_INIT(__CALLBACK) uart0_drive_mode_init(__CALLBACK)
//USER0
#define USER_UART0_CFG(_CONFIG,_BAUDRATE,_Rang,_CALLBACK) uart0_cfg((_CONFIG),(_BAUDRATE),(_Rang),(_CALLBACK))
#define USER_UART0_TXD_QUERY(_BUFFER,_TXD_NUM) uart0_txd_query((_BUFFER),(_TXD_NUM))
#define USER_UART0_ENABLE_RX_INTERRUPT(_LEVEL) uart0_enable_rx_interrupt(_LEVEL)
#define USER_UART0_DISABLE_RX_INTERRUPT() uart0_disable_rx_interrupt()
#define USER_UART0_SET_TX_INTERRUPT_LEVEL(_LEVEL) uart0_set_tx_interrupt_level(_LEVEL)
#define USER_UART0_TXD_INTERRUPT_START(__BUFFER,__TXD_NUM) uart0_txd_interrupt_start((__BUFFER),(__TXD_NUM))
Explanation:
Uart0_drive_mode_init(), the module initialization function, needs to pass a void (*pRxCallBack)(uint8_t) type function pointer. This function is to receive the interrupt callback user receive processing function, and put the RXD0 data into the ring receive buffer area;
Uart0_cfg () configuration function, need to call back the user's I / O port configuration function, because the UARTI / O port is optional (lazy)
Uart0_txd_query() query send function, thread safe
Uart0_enable_rx_interrupt() enables receiving interrupts and sets priorities
Uart0_disable_rx_interrupt() turns off the receive interrupt
Uart0_set_tx_interrupt_level() sets the sending priority
Uart0_txd_interrupt_start() interrupt transmission, thread safe
Then, I wrap all the interrupts and functions into the underlying layer.
There is a point here. I don't understand how to do it. How is this receiving ring buffer designed to achieve decoupling?
"------------------------------------------------- ------------------------ Gorgeous dividing line ----------------------- -------------------------------------------------- -----"
I have been learning about OOPC and data structure recently, and suddenly realized how to separate the ring buffer. The original method is so simple. I guess it was complicated before.
First we define a queue class that defines four interfaces:
Bool init_byte_queue(byte_queue_t* ptQueue,uint8_t* pchBuffer,uint16_t hwSize);
Bool is_queue_empty(byte_queue_t* ptQueue);
Void enqueue_byte(byte_queue_t* ptQueue,uint8_t chInData);
Void dequeue_byte(byte_queue_t* ptQueue,uint8_t* pchOutData);
Then define a data structure:
Struct byte_queue_t{
Uint8_t *pchBuffer;
Uint16_t hwSize;
Uint16_t hwHead;
Uint16_t hwTail;
Uint16_t hwLength;
};
The implementation is as follows:
#define this (*ptThis)
Bool init_byte_queue(byte_queue_t* ptQueue,uint8_t* pchBuffer,uint16_t hwSize)
{
CLASS(byte_queue_t) *ptThis = (CLASS(byte_queue_t) *)ptQueue;
If(NULL == ptQueue){
Return false;
}
this.pchBuffer = pchBuffer;
this.hwSize = hwSize;
this.hwHead = 0;
this.hwTail = 0;
this.hwLength = 0;
}
Bool is_queue_empty(byte_queue_t* ptQueue)
{
CLASS(byte_queue_t) *ptThis = (CLASS(byte_queue_t) *)ptQueue;
If(NULL == ptQueue){
Return true;
}
Return (0 == this.hwLength);
}
Void enqueue_byte(byte_queue_t* ptQueue,uint8_t chInData)
{
CLASS(byte_queue_t) *ptThis = (CLASS(byte_queue_t) *)ptQueue;
If(NULL == ptQueue){
Return;
}
this.pchBuffer[this.hwHead] = chInData;
this.hwHead++;
If(this.hwHead 》= this.hwSize){
this.hwHead = 0;
}
this.hwLength++;
}
Void dequeue_byte(byte_queue_t* ptQueue,uint8_t* pchOutData)
{
CLASS(byte_queue_t) *ptThis = (CLASS(byte_queue_t) *)ptQueue;
If(NULL == ptQueue){
Return;
}
If(NULL == pchOutData){
Return;
}
If(!this.hwLength){
Return;
}
*pchOutData = this.pchBuffer[this.hwTail];
this.hwTail++;
If(this.hwTail 》= this.hwSize){
this.hwTail = 0;
}
this.hwLength--;
}
At this point, the basic UART is finished, but I read the issue of the synchronous locking of the ring queue today. It is necessary to add it.
The circular queue above is problematic. What is the problem? Is the synchronization problem of hwLength, hwLength--and hwLength++
Located in different threads, so you need to lock to ensure atomicity issues.
QUEUE_ENTER_CRITICAL();
this.hwLength--; // must guarantee atomicity
QUEUE_LEAVE_CRITICAL();
//QUEUE_ENTER_CRITICAL();
this.hwLength++; / / placed in the interrupt, its own task priority is high
//QUEUE_LEAVE_CRITICAL();
If the write and read thread priorities are level, then you need to lock; if there is a higher priority, a lower priority, that lower priority
Threads need to be locked, high priority is not needed.
Fiber Optic Splice Closure,Fiber Optic Splice Case,Fiber Splice Closures,Outdoor Fiber Optic Splice Closure
Cixi Dani Plastic Products Co.,Ltd , https://www.danifiberoptic.com