“Embedded systems have been widely used in defense electronics, digital home, industrial automation, automotive electronics and other fields. In the process of embedded development, many systems usually use serial port drivers to meet communication requirements, but in practical applications, using SPI communication will be more efficient and faster. SPI interface is a high-speed and efficient serial interface technology, so SPI devices are very convenient in data communication applications.Based on S3C2440 of ARM9 chip and Linux operating system, this paper designs a SPI driver, which is reliable, flexible, and easy to transplant.
“
Embedded systems have been widely used in defense electronics, digital home, industrial automation, automotive electronics and other fields. In the process of embedded development, many systems usually use serial port drivers to meet communication requirements, but in practical applications, using SPI communication will be more efficient and faster. SPI interface is a high-speed and efficient serial interface technology, so SPI devices are very convenient in data communication applications. Based on the S3C2440 of the ARM9 chip and the Linux operating system, this paper designs a SPI driver, which is reliable, flexible, and easy to transplant. It can be applied to a variety of embedded platforms to realize the communication between the arm and the device.
1 Hardware Description
1.1 S3C2440 Development Platform
Using Samsung’s SoC chip S3C2440[4]As the core processor, the main frequency is 400 MHz, and it forms the core part together with 64 MB SDRAM and 64 MB NAND Flash. In addition, the platform also provides users with a large number of communication, Display, debugging and I/O interfaces. In order to meet the design needs, the Linux2.6.21 kernel is transplanted on this platform.
1.2 SPI hardware module
S3C2440 has two SPIs, each SPI has two 8-bit shift registers for sending and receiving data independently, and is compatible with the SPI ver.2.11 protocol, supports 8-bit logic prescaler, and the system can use polling, interrupt, DMA three A way to judge the SPI sending and receiving status.This SPI module contains the following signal lines[5]:
(1) SCK: data synchronization clock signal, driven by the master device and output to the slave device, so that the slave device receives or sends data according to the pace of the synchronization clock.
(2) nCS (GPIO specified by the user): The slave device selection signal line (Slave Select, SS) is sent by the master device to select and activate a slave device, and the low level is active.
(3) MISO (SPIMISO0): Master input and slave output signal line, indicating that the signal is used as input in the master device and as output in the slave device.
(4) MOSI (SPIMOSI0): master-out-slave-in signal line, indicating that the signal is used as output in the master device and as input in the slave device.
(5)/SS(nSS): Multi-master error detection.
2 SPI device driver design under Linux
Linux device drivers play an important role in the Linux kernel. It enables some specific hardware to respond to a well-defined internal programming interface that completely hides the details of how the device works.User actions are performed through a standardized set of calls that are formally independent of a particular driver, and it is the driver’s task to map these calls to operations specific to the actual hardware device[6]. The SPI driver of this design mainly defines three operations: initialization, read and write. The initialization operation is used to initialize some kernel mechanisms and memory when the driver is loaded into the kernel for the first time. The write operation is responsible for copying user data to the kernel buffer and controlling the local master SPI to send data to the slave SPI registers. The read operation will continuously read the data received in the local master SPI according to the number of bytes read by the user, and copy it to the user space. The driver will use an interrupt to notify the system whether the SPI data has been sent, that is, when the SPI hardware module has sent a piece of data, it will initiate an interrupt to the system through the interrupt line. After the system responds to the interrupt, the driver will call the interrupt processing routine.
2.1 SPI initialization
(1) The application is interrupted. This driver design judges whether the data has been sent through interrupts, so it is necessary to apply for SPI0-related interrupts and register the corresponding interrupt handling functions. The interrupt handler for this driver is declared as follows:
static irqreturn_t s3c2440_isr_spi(int irq, void*dev_id, struct pt_regs*reg)
Use request_irq to apply for an interrupt number to the kernel and register an interrupt handler:
request_irq(IRQ_SPI0, s3c2440_isr_spi, SA_INTERRUPT, DEVICE_NAME, s3c2440_isr_spi);
(2) Virtual address mapping. The driver can directly access the register corresponding to the physical address of the device by accessing the virtual address in the kernel, and operate it. The address mapping process of the SPI device is as follows:
request_mem_region(S3C2440_PA_SPI, 0x30, “s3c2440-spi”);
base_addr = ioremap(S3C2440_PA_SPI, 0x30);
Among them, S3C2440_PA_SPI is the physical address of the SPI (defined in /asm-arch/arch-s3c2440/map.h), and a memory area of 0x30 size is allocated from S3C2440_PA_SPI, and then moved to the kernel space.
(3) Setting of related registers. Set the SPI working mode by configuring the SPI function register. Using the virtual address returned by ioremap as the base address, access the corresponding registers by adding different offsets. In this design, the local SPI is set as the master device, the SCK signal is enabled, and both CPOL and CPHA are set to 0, and the SPI works in normal mode. Set the divider ratio in the baud rate prescaler register (SPPRE) to 8. The specific design is as follows:
__raw_writel((S3C2440_SPCON_SMOD_INT|S3C2440_SPCON_ENSCK|S3C2440_SPCON_MSTR), s3c2440_SPCON);
DPRINTK(DEVICE_NAME”SPCON initializen”);
__raw_writel((S3C2440_SPPIN_ENMUL | S3C2440_SPPIN_KEEP), s3c2440_SPPIN);
DPRINTK(DEVICE_NAME”SPPIN initializen”);
__raw_writel(0x07, s3c2440_SPPRE);
DPRINTK(DEVICE_NAME”SPPRE initializen”);
(4) Initialize the send and receive data buffers. The data buffer uses the ring buffer structure, and realizes the dynamic management of the buffer through the circular movement of the head and tail pointers. It is defined as follows:
typedef struct
{
spi_buf buf[MAX_SPI_BUF];
unsigned int head, tail;
wait_queue_head_t wq;
} SPI_BUF; static SPI_BUF spi_Tx_buf; static SPI_BUF spi_Rec_buf;
Among them, spi_buf represents the char type, and MAX_SPI_BUF is the buffer size, which is set to 1 024 B. head and tail respectively represent the subscripts of the head and tail arrays, and wq is the head of the waiting queue. This structure is managed by the following macros:
#define SPI_Tx_BUF_HEAD(spi_Tx_buf.buf[spi_Tx_buf.head])
#define SPI_Tx_BUF_TAIL(spi_Tx_buf.buf[spi_Tx_buf.tail])
#define INCBUF(x, mod)((++(x))&((mod)-1))
The first two macros are used to refer to the elements in the buffer, and the last macro is used to advance the head and tail subscripts, and ensure that the head and tail subscript values can be changed cyclically without overflow.
During initialization, the head and tail pointers of the receive and transmit buffers are cleared respectively, as follows:
spi_Tx_buf.head=spi_Tx_buf.tail=0;spi_Rec_buf.head=spi_Rec_buf.tail=0;
(5) Initialization of data structures related to the kernel mechanism. The kernel mechanism used in this design includes the operation of the upper and lower parts of the interrupt and the sleep waiting mechanism. Therefore, it is necessary to initialize the sending and receiving waiting queues and the tasklet structure, and register the tasklet processing function. The initialization process is as follows:
init_waitqueue_head(&(spi_Tx_buf.wq));
init_waitqueue_head(&(spi_Rec_buf.wq));
tasklet_init(&spi_tasklet, spi_tasklet_handler, data); (6) Initialize the corresponding port. According to the external pin configuration of S3C2440, set the GPIO that is multiplexed with the SPI function pin as the corresponding SPI function. The specific operations are as follows:
s3c2440_gpio_cfgpin
(S3C2440_GPE11, S3C2440_GPE11_SPIMISO0);
s3c2440_gpio_cfgpin
(S3C2440_GPE12, S3C2440_GPE12_SPIMOSI0);
s3c2440_gpio_cfgpin
(S3C2440_GPE13, S3C2440_GPE13_SPICLK0);
s3c2440_gpio_cfgpin
(S3C2440_GPG2, S3C2440_GPG2_INP); //set nSS
s3c2440_gpio_cfgpin(S3C2440_GPB10,
S3C2440_GPB10_OUTP); //Set the chip select signal
s3c2440_gpio_setpin(S3C2440_GPB10, 1);
2.2 SPI write operation
The write operation is mainly to copy the data in the user space of the upper application part to the ring buffer in the kernel space, and then send the data in the buffer to the SPI send register. After the SPI sends a data, the system generates an interrupt. The second half of the interrupt routine will call the tasklet to determine the buffer status. If there is corresponding space in the buffer, the next data can be filled into the SPI transmit register until all the data in the buffer is sent.
The write operation of this design realizes the dynamic management of the ring buffer, that is, when the buffer deletes data and the tail pointer moves forward, it is allowed to add data to the buffer, and the head pointer moves forward. This design can make the user space task and the kernel space data transmission at the same time, improve the user space task execution efficiency, and when the copy_from_user function is used to copy the data from the user space to the kernel space, the data transmission is still in progress, that is, the data is sent from the user space. The space-to-kernel space copying process is concurrent with the data sending process, which improves driver efficiency.
In order to realize the dynamic management of the ring buffer, two functions, copy_to_Tx_buf_init and copy_to_Tx_buf, are defined to complete the copy operation of data to the buffer.
(1) copy_to_Tx_buf_init function. This function is mainly used in two cases:
① If the buffer is empty, when a group of data arrives and the size of the data is smaller than the space size of the buffer, the data is directly put into the buffer.
② If the size of the sent data is larger than the space of the remaining buffer, only the data of the buffer size will be copied to the buffer.
When the buffer is full, the process sleeps until all data in the buffer is sent, the buffer is empty again, the current process is awakened, and the unsent part of this group of user data is copied to the buffer and continues to be sent.
(2) copy_to_Tx_buf function. This function is mainly used when the buffer is being sent and has not been sent, and copies a new set of user data to the buffer. First calculate the remaining space of the buffer. If the remaining space is greater than the size of the user data in this group, copy all the user data to the buffer directly; if the remaining space is smaller than the size of the data in this group, copy the user data with the same size as the remaining space to the buffer. .
The specific process of the write operation is shown in Figure 1. First, the user data is converted from the space state to the kernel state, and the corresponding receive flag is set. Then judge the data size. If the data is larger than the buffer space, the data overflows and the write operation ends; if there is no overflow, in order to ensure the data between the processes, the process obtains a spin lock, and at this time, it is judged whether the buffer is empty. According to the introduction of the above two functions, different functions are called under different circumstances. After the data is written to the ring buffer, the data is sent to the SPI transmit register. When the SPI transmit register sends data, the ring buffer still receives data. If the buffer is full at this time, the spin lock is released, and the process wait flag (wait_Tx_done) is set to sleep the process until the data in the transmit register is sent. After sending, wake up the process again, and judge whether all the data has been sent. If there is still data waiting to be sent, copy_to_Tx_buf_int is called; if all data has been sent, the write operation ends. If the buffer is not full, it is judged whether the data has been sent. After all data is sent, the sending operation ends.
The Links: PM10CHA060 FP15R12W1T4