How to receive 115200bps data via UART using CC2540
[Copy link]
This question is quite interesting and has a certain universality, so I wrote it down and shared it with everyone.
I recently did an IoT project, the purpose of which was to add Bluetooth functionality to a device that could only be controlled via a UART interface.
The main structure is to connect the CC2540 module and the device through UART, and then implement the corresponding Profile on CC2540. The mobile phone communicates with CC2540 through Profile, and CC2540 communicates with the device through UART interface. In this way, after the conversion of CC2540, the device can be controlled by mobile phone APP.
cc2540 is TI's Bluetooth 4.0 (BLE) SOC chip with the following features:
- 256KB FALSH
- 16KB RAM
- 32MHz single-cycle enhanced 51 Core
- The Bluetooth protocol stack and user application can run on a single chip
- Extremely low power consumption
Actually, the whole project is quite ordinary and not difficult, except for one thing, which is to use CC2540 to capture UART data. The device data packet size here is not fixed, and the maximum does not exceed 1KB.
Isn't it easy to receive UART data? A microcontroller can do it, so why is there a problem here?
Because CC2540 is a SOC chip, that is, a System on chip. In addition to running user applications, CC2540 also runs the Bluetooth 4.0 protocol stack, and all events related to sending and receiving wireless data packets are automatically handled by the protocol stack.
OK, here comes the problem. According to TI, each packet-related event may take up to several milliseconds of CPU time , and interrupts are completely disabled during this time.
Usually, we receive data from UART, most commonly through interrupts. Configure the device's registers so that each time UART receives data, an interrupt is generated, and then the data is read in the interrupt.
At a rate of 115200bps, about 115200/8 = 14400 bytes of data are sent in 1 second, and 1ms is 14 bytes. If the data is received through interrupts, assuming that the CPU takes 1ms to process and run the protocol stack, and data happens to come in at this time, 14 bytes will be lost, which is definitely unusable. So, this method is blocked.
Fortunately, there is a better way, which is to receive data through DMA
DMA (Direct Memory Access) is an important feature of all modern computers. It allows hardware devices of different speeds to communicate without relying on a large interrupt load on the CPU. Otherwise, the CPU needs to copy each piece of data from the source to the register and then write it back to the new place again. During this time, the CPU is unavailable for other work.
The characteristic of DMA is that it can transfer data without going through the CPU, which is suitable for the current situation where the CPU is too busy. According to the DataSheet, CC2540 has a total of 4 DMA channels, and the wireless protocol stack uses one.
By setting the DMA trigger source to UART接收到数据 the UART data register, the transmission source address to the UART data register, the transmission destination address to a buffer, and setting the source address to remain unchanged and the destination address to increase automatically each time, you can realize DMA automatically moving UART data.
However, no matter how big the DMA receive buffer is, it will eventually be full. When the DMA buffer is full or the data in the DMA buffer needs to be processed, the DMA transmission needs to be suspended. If there is any data entering at this time, it will be lost.
To solve this problem, the classic Ping-Pang DMA operation method is used here.
Ping-Pang enables dual DMA channels to ensure that there is always one DMA channel receiving data at any time, so data will not be lost. In addition, through Ping-Pang switching, the DMA Buf will not be full, so continuous reception is possible.
In the specific implementation here, three buffers, two secondary ( a , b ) and one main ( x ), are opened first, and two DMA channels are established to transfer DMA data to the two secondary buffers a and b respectively . A timer is used to switch between the two DMA channels. After each switch to a new secondary buffer, the data in the old secondary buffer is exported to the main receiving buffer x . In this way, the data can be processed in x without losing data in the process.
But there is still a pit, that is, how to get the length of DMA transfer data
When using the Ping-Pang method to operate DMA, you need to continuously export the data that has been saved in the DMA Buf. At this time, you need to know the length of the data in the DMA Buf. The DMA controller of some chips has a related interface that can directly read the transfer count value from the register. However, CC2540 does not have this function. TI, TI, you do things halfway, is it really good-_-#
Obviously, this cannot be achieved by initializing the DMA receive buffer to 0 and then determining the length of the non-zero data, because the data received by the UART may be 0.
Later, I thought of a solution. The idea is to initialize the data in the DMA receive buffer to a fixed value, such as 0x00, before starting each round of DMA transmission. Then configure the DMA controller to transmit double-byte data 1 each time. By querying the DataSheet, the byte after the data register of the UART controller is part of the baud rate register, which is a fixed value and not zero. In this way, each DMA transmission will transmit 1 byte of UART received data + 1 byte of fixed data to the receive Buf. By analyzing the length of the received fixed data, the length of the data received by DMA can be known.
There are three things to note here:
- Receiving data via DMA
- DMA Ping-Pong Operation
- Implement DMA transfer counting by adding a "tail" to the data
The above method perfectly solves the problem of UART receiving data. Now Soc chips with wireless protocol stack are widely used, and the problem of receiving data of this type of chip can be solved by this idea.
|