1. Preface
Previous: https://blog.csdn.net/ylc0919/article/details/111050124
It's been ten months since I said I would send the second generation framework. It's hard to have time to summarize the harvest during this period. After writing the first generation AT queue, I have many ideas. For example, can one AT queue manage multiple AT modules; For example, the cyclic queue plus cyclic buffer of the AT framework takes up a lot of space. It can only write the corresponding serial number of the data to the queue, and temporarily fill the data frame when reading the queue; For example, if queues are used in many parts of the code, can you write a queue framework, copy the code directly each time, or use pointers to realize that multiple queues share a queue framework
After such a long time of practical verification, most of these conjectures have come to fruition.
2. Conclusion
In accordance with the Convention, the first conclusion is:
1. It is not practical for an AT queue to control multiple AT modules, because one AT module is stuck, which will cause all modules to fail to move. For EC600, it is normal for a network injection instruction to take more than ten seconds when the signal is poor. AT this time, the queue cannot process other instructions.
2. Cancel the circular buffer. Only the command sequence number is stored in the queue, which must be considered according to the situation. If the data to be processed has little correlation with time and the format is fixed, it is a very good way to store only the command serial number in the queue. If the data is changing all the time, this method is not very easy to use, but it is not unusable. You can open another queue in flash and write the current data to flash first, so as to not only maintain the power down, but also save memory.
3. Using pointers to implement multiple queues sharing a queue framework is no problem in theory, but I haven't found a simple and easy way to use. The code written is not as good as directly copying the queue framework and changing its name
4. Sort out a queue framework, which can be copied and modified directly when used, and can be used flexibly, which is exactly what this paper wants to write.
3. Circular queue
Next, put the code of each part of the circular queue, and make comments in important places.
3.1 write queue to queue header
The head of the queue runs ahead. If you catch up with the end of the queue, you will eat one.
void write_to_queue(MY_INFO_t info) { //When the queue is full, the first written data will be overwritten (you can also return directly and ignore the newly added data) if(my_queue.rear==((my_queue.front+1)%MY_QUEUE_MAXNUM)) { my_queue.rear=(my_queue.rear+1)%MY_QUEUE_MAXNUM; } //Write queue my_queue.info_queue[my_queue.front]=info; //The queue head enters a grid my_queue.front=(my_queue.front+1)%MY_QUEUE_MAXNUM; }
3.2 read queue from tail
If the tail of the queue catches up with the head of the queue, it means that there is no data in the queue.
If you run the system, you can directly open a thread and put it in.
If the bare metal runs, it can be put into the 10ms event.
//The read queue is one tick for each call, and the timeout is n ticks void read_from_queue(void) { if(my_queue.flags.busy==0){ //Queue idle if(my_queue.rear!=my_queue.front){ my_queue.flags.busy=1; my_queue.info =my_queue.info_queue[my_queue.rear]; my_queue.otime =0; my_queue.retryNum =0; my_queue.rear =(my_queue.rear+1)%MY_QUEUE_MAXNUM; //The queue follows a grid //data processing } } else{ //Queue busy my_queue.otime++; //ticks+1 if(my_queue.otime>my_queue.info.oTime){ //The time is up my_queue.otime=0; if(my_queue.retryNum<my_queue.info.retryNum){//If the maximum number of retries is not reached my_queue.retryNum++; //retry } else{ //Exception, retry reaches the upper limit, call error handling queue_error(); //error handling my_queue.retryNum=0; my_queue.flags.busy=0; } } } }
3.3 get the number of data in the current queue
Mainly used for debugging
u16 get_queue_data_num(void) { u16 ret=0; if(my_queue.front>my_queue.rear) ret=my_queue.front-my_queue.rear; else if(my_queue.front<my_queue.rear) ret=MY_QUEUE_MAXNUM-my_queue.rear+my_queue.front; else if(my_queue.front==my_queue.rear) ret=0; return ret; }
3.4 emptying the queue
Just clear the queue structure directly
void clear_my_queue(void) { memset((void*)&my_queue,0,sizeof(MY_QUEUE_t)); }
3.5 two important structures
This is the content of each piece of data to be filled in the queue. You can directly add variables here
typedef struct { u8 retryNum; //Maximum number of retries u16 cmd; //Information id u16 oTime; //Timeout unit: ticks }MY_INFO_t;
This is the queue control structure,
typedef struct { struct{ u8 busy:1; }flags; u8 retryNum; //Current number of retries u8 rstNum; //Reset times, special treatment for multiple failures (not used yet) MY_INFO_t info_queue[MY_QUEUE_MAXNUM]; //All instruction ring queue MY_INFO_t info; //Currently fetched instruction information u16 otime; //Current command wait time u16 front; //Queue header array subscript u16 rear; //Queue tail array subscript }MY_QUEUE_t;
4. Effects and examples
There are too many codes. Only some screenshots are selected for display.
4.1 three read queue threads
Under LiteOS, I directly open a separate thread for each read queue.
4.2 AT frame write queue and EC200 initialization
To simplify the initialization code, I did not use a structure as a write queue parameter.
Instead of using strlen to get the length in the write queue function, I prefer to pass it in as a parameter.
4.3 AT frame read queue
Read the queue. There's nothing to say
4.4 EC200 maintains TCP long connection
Just follow the AT instruction manual
5. Download
5.1 circular queue
https://download.csdn.net/download/ylc0919/44922216
5.2 TCP long connection of at framework + EC200 (common with EC600)
https://download.csdn.net/download/ylc0919/44922750
It is necessary to refer to EC200At instruction manual.