Network Security Internet Technology Development Database Servers Mobile Phone Android Software Apple Software Computer Software News IT Information

In addition to Weibo, there is also WeChat

Please pay attention

WeChat public account

Shulou

How to drive SPI under Linux

2025-03-29 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Servers >

Share

Shulou(Shulou.com)06/01 Report--

How to drive SPI under Linux? in view of this problem, this article introduces the corresponding analysis and solution in detail, hoping to help more partners who want to solve this problem to find a more simple and feasible method.

1. SPI bus

1.1. Overview of SPI bus

SPI, which is the abbreviation of English Serial Peripheral interface, is the serial peripheral interface as its name implies. It was first defined by Motorola on its MC68HCXX family of processors. SPI interface is mainly used in EEPROM,FLASH, real-time clock, AD converter, digital signal processor and digital signal decoder. SPI is a kind of high-speed, full-duplex, synchronous communication bus, and occupies only four lines on the pins of the chip, which saves the pins of the chip, and at the same time saves space and provides convenience for the layout of PCB. Because of this easy-to-use feature, more and more chips integrate this communication protocol. The composition and signal type of the SPI bus are shown in figure 1-1:

MOSI-master device data output, slave device data input corresponding to MOSI master output slave input

MISO-master device data input, slave device data output corresponding to MISO master input slave output

CLK-clock signal, generated by the master device

NCS-Slave enable signal, controlled by master device

Figure 1-1 SPI bus model

1.2. SPI bus timing

The slave equipment enable signal and clock signal generated by the SPI interface under the control of Master, the two bidirectional shift registers transmit data by bit to exchange data, the high bit is in the front (MSB first), and the low bit is in the back. As shown in the following figure, the data changes on the descending edge of the CLK, and one bit of the rising edge data is stored in the shift register.

Figure 1-2 spi transmission sequence diagram

In a SPI clock cycle, the following operations are completed: (1) Master sends 1-bit data over the Mosi line, while Slave reads the 1-bit data through the Mosi line; (2) Slave sends 1-bit data through the Miso line, while Master reads the 1-bit data through the Miso line. Master and Slave each have a shift register, as shown in figure 1-3, and the two shift registers are connected into a ring. According to the change of CLK, the data is moved out of the Master register and Slave register in the way of MSB first, and into the Slave register and Master register in turn. When all the contents of the registers are removed, the exchange of the contents of the two registers is completed.

1.3. SPI bus transfer mode

There are four modes of SPI bus transmission, which are defined by clock polarity (CPOL,Clock Polarity) and clock phase (CPHA,Clock Phase) respectively, in which the CPOL parameter specifies the level of the idle state of the SCK clock signal, and the CPHA specifies whether the data is sampled at the rising edge or falling edge of the SCK clock. The timing diagram of these four modes is shown in figure 1-4 below:

The pattern 0rig CPOL = 0pm CPHANG0. The idle CLK serial clock line is low, data is sampled at the rising edge of the SCK clock, and the data is switched at the falling edge of the CLK clock.

The pattern 1 is CPOL = 0pm CPHANG1. The idle CLK serial clock line is low, data is sampled at the falling edge of the SCK clock, and the data is switched at the rising edge of the CLK clock.

The pattern 2 is CPOL = 1 pm CPHANG0. The idle CLK serial clock line is high, data is sampled at the falling edge of the SCK clock, and the data is switched at the rising edge of the CLK clock.

The mode 3 is CPOL = 1, and the pattern is CPHA. The idle CLK serial clock line is high, the data is sampled at the rising edge of the SCK clock, and the data is switched at the falling edge of the CLK clock. The more common modes are mode 0 and mode 3. To describe the timing of the SPI bus more clearly, figure 1-5 of the SPI timing in mode 0 is shown below:

Figure 1-5 SPI sequence diagram under mode0

1.4. Advantages and disadvantages of SPI bus

The main results are as follows: (1) in point-to-point communication, SPI interface does not need addressing operation, and it is full-duplex communication, which is simple and efficient. (2) the SPI interface does not have the specified flow control, and there is no reply mechanism to confirm whether the data is received.

2. Linux SPI framework

2.1. Software architecture

The Linux system has good support for spi devices, and the spi driver under linux system can be logically divided into three parts:

Hongmeng official Strategic Cooperation to build HarmonyOS Technology Community

Spi core (SPI Core): SPI Core is the core part of the Linux kernel used to maintain and manage spi. SPI Core provides operational interface functions that allow an spi master,spi driver and spi device to register in SPI Core when initialized and log out when exiting.

Spi Controller driver (SPI Master Driver): SPI Master implements the hardware access operation of spi bus for different types of spi controller hardware. SPI Master registers a controller with SPI Core through the interface function.

Spi device driver (SPI Device Driver): SPI Driver is the driver corresponding to the spi device side and registers with SPI Core through the interface function. The function of SPI Driver is to connect the spi device to the spi bus. The software architecture diagram of Linux is shown in figure 2-1:

Figure 2-1 spi software architecture diagram

2.2. Initialization and exit proc

2.2.1. Register the spi controller

Registering the spi controller to the kernel is divided into two phases: the first stage, using spi_alloc_master, allocates a spi_master space, as shown in figure 2-2:

In the second stage, the spi_master allocated in the first phase is registered with the kernel using spi_register_master, as shown in 2-3:

2.2.2. Log out of the spi controller

The process of logging out of the spi controller is shown in figure 2-4:

2.3. Key data structure

2.3.1. Spi_device

Struct spi_device {device structure corresponding to struct device dev; / * spi controller struct spi_master * master; / * the master structure used by the device, under which main controller is it hung * / U32 max_speed_hz; / * maximum communication clock frequency * / U8 chip_select / * selected number, each master supports multiple spi_device * / U8 mode # define SPI_CPHA 0x01 / * clock phase * / # define SPI_CPOL 0x02 / * clock polarity * / # define SPI_MODE_0 (0 | 0) / * (original MicroWire) * / # define SPI_MODE_1 (0 | SPI_CPHA) # define SPI_MODE_2 (SPI_CPOL | 0) # Define SPI_MODE_3 (SPI_CPOL | SPI_CPHA) # define SPI_CS_HIGH 0x04 / * chipselect active high? * / # define SPI_LSB_FIRST 0x08 / * per-word bits-on-wire * / # define SPI_3WIRE 0x10 / * SI/SO signals shared * / # define SPI_LOOP 0x20 / * loopback mode * / # define SPI_NO_CS 0x40 / * 1 dev/bus No chipselect * / # define SPI_READY 0x80 / * slave pulls low to pause * / U8 bits_per_word / * the number of bits per word length. Default is 8 bits / int irq; void * controller_state; / * controller status * / void * controller_data; / * controller data * / char modalias [SPI _ NAME_SIZE] / * name of the device driver * / int cs_gpio / * chipselect gpio * / * * likely need more hooks for more protocol options affecting how * the controller talks to each chip, like: *-memory packing (12 bit samples into low bits, others zeroed) *-priority *-drop chipselect after each word *-chipselect delays * -. * /}

Spi_device represents a peripheral spi device, which is generated by scanning the linked list of devices generated by registered devices in BSP and registering with spi_bus after the completion of master controller driver registration. In the kernel, each spi_device represents a physical spi device.

2.3.2. Spi_driver

Struct spi_driver {spi_device device Table supported by const struct spi_device_id * id_table; / * / int (* probe) (struct spi_device * spi); int (* remove) (struct spi_device * spi); void (* shutdown) (struct spi_device * spi) Int (* suspend) (struct spi_device * spi, pm_message_t mesg); int (* resume) (struct spi_device * spi); struct device_driver driver;}

Spi_driver represents a SPI protocol drivers, that is, a peripheral driver

2.3.3. Struct spi_master

Struct spi_master {device structure of struct device dev; / * spi controller * / struct list_head list; / * linked list / * other than negative (= = assign one dynamically), bus_num is fully * board-specific. Usually that simplifies to being SOC-specific. * example: one SOC has three SPI controllers, numbered 0.. 2, * and one board's schematics might show it using SPI-2. Software * would normally use bus_num=2 for that controller. * / s16 bus_num; / * bus (or controller number) * / * chipselects will be integral to many controllers; some others * might use board-specific GPIOs. * / U16 num_chipselect; / * number of selected pieces * / / * some SPI controllers pose alignment requirements on DMAable * buffers; let protocol drivers know about these requirements. * / u16 dma_alignment; / * spi_device.mode flags understood by this controller driver * / u16 mode_bits; / * master supported device mode * / / * bitmask of supported bits_per_word for transfers * / U32 bits_per_word_mask / * other constraints relevant to this driver * / U16 flags / * Flag bit # define SPI_MASTER_HALF_DUPLEX BIT (0) / * can't do full duplex * / # define SPI_MASTER_NO_RX BIT (1) / * can't do buffer read * / # define SPI_MASTER_NO_TX BIT (2) / * can't do buffer write * / / * lock and mutex used to limit certain restrictions For SPI bus locking * / spinlock_t bus_lock_spinlock Struct mutex bus_lock_mutex; / * flag indicating that the SPI bus is locked for exclusive use * / bool bus_lock_flag; / * Setup mode and clock, etc (spi driver may call many times). * * IMPORTANT: this may be called when transfers to another * device are active. DO NOT UPDATE SHARED REGISTERS in ways * which could break those transfers. * / int (* setup) (struct spi_device * spi); / * update the hardware configuration based on spi devices. Set spi working mode, clock, etc. * / * bidirectional bulk transfers * * + The transfer () method may not sleep; its main role is * just to add the message to the queue. * + For now there's no remove-from-queue operation, or * any other request management * + To a given spi_device, message queueing is pure fifo * * + The master's main job is to process its message queue, * selecting a chip then transferring data * + If there are multiple spi_device children, the queue * arbitration algorithm is unspecified (round robin, fifo, * priority, reservations, preemption Etc) * * + Chipselect stays active during the entire message * (unless modified by spi_transfer.cs_change! = 0). * + The message transfers use clock and SPI mode parameters * previously established by setup () for this device * / int (* transfer) (struct spi_device * spi, struct spi_message * mesg); / * the method of adding messages to the queue. This function cannot sleep. Its job is to arrange the transfer that occurs and call the registered callback function complete () * / * called on release () to free memory provided by spi_master * / void (* cleanup) (struct spi_device * spi); / * the cleanup function is called in the spidev_release function, and spidev_release is registered as the release function of spidev. * / * These hooks are for drivers that want to use the generic * master transfer queueing mechanism. If these are used, the * transfer () function above must NOT be specified by the driver. * Over time we expect SPI drivers to be phased over to this API. * / bool queued; struct kthread_worker kworker; / * work queue thread for managing data transfer message queues * / struct task_struct * kworker_task; struct kthread_work pump_messages; / * work queues that implement data transfer queues * / spinlock_t queue_lock Struct list_head queue; / * message queues for the controller, all queues waiting for transmission are hung under the linked list * / struct spi_message * message queues currently being processed by cur_msg;/* * / bool busy; / busy * / bool running / * running * / bool rt; int (* prepare_transfer_hardware) (struct spi_master * master); / * callback function, which is called before officially initiating the transfer to prepare hardware resources * / int (* transfer_one_message) (struct spi_master * master, struct spi_message * mesg) / * the atomic transmission callback function of a single message. Each message in the queue will call back once to complete the transmission work * / int (* unprepare_transfer_hardware) (struct spi_master * master); / * clean up the callback function * / / * gpio chip select * / int * cs_gpios;}

Spi_master represents a spi controller.

2.3.4. Struct spi_message and spi_transfer

To complete the data transfer with the SPI device, we need two other data structures: spi_message and spi_transfer.

Spi_message contains a sequence of spi_transfer structures. Once the controller receives a spi_message, the spi_transfer in it should be sent sequentially and cannot be interrupted by other spi_message, so we think that spi_message is an atomic operation of SPI data exchange. Let's take a look at the definitions of these two data structures:

Struct spi_message:

Struct spi_message {struct list_head transfers; / * spi_transfer linked list queue, the transmission segment queue of this message. A message can contain multiple transport segments. * / struct spi_device * spi; / * the destination device * / unsigned is_dma_mapped:1; / * if true, this call provides dma and cpu virtual addresses. * / * REVISIT: we might want a flag affecting the behavior of the * last transfer. Allowing things like "read 16 bit length L" * immediately followed by "read L bytes". Basically imposing * a specific message scheduling algorithm. * * Some controller drivers (message-at-a-time queue processing) * could provide that as their default scheduling algorithm. But * others (with multi-message pipelines) could need a flag to * tell them about such special cases. * / / * completion is reported through a callback * / void (* complete) (void * context); / * callback function after asynchronous call * / void * context; / * callback function parameter * / unsigned actual_length / * actual transmission length * / int status; / * the result of the message is successfully set to 0, otherwise it is a negative error code. * / * for optional use by whatever driver currently owns the * spi_message... Between calls to spi_async and then later * complete (), that's the spi_master controller driver. * / struct list_head queue; void * state;}

The linked list field queue is used to hang the structure on the queue field representing the spi_master structure of the controller, and the controller can be queued by adding multiple spi_message at the same time. Another linked list field, transfers, is used to link the spi_tranfer structure hanging under this message. The complete callback function is called when all spi_transfer under the message are transferred to inform the protocol driver to process the received data and prepare the next batch of data to be sent. Let's take a look at the spi_transfer structure: spi_transfer

Struct spi_transfer {/ * it's ok if tx_buf = = rx_buf (right?) * for MicroWire, one buffer must be null * buffers must work with dma_*map_single () calls, unless * spi_message.is_dma_mapped reports a pre-existing mapping * / const void * tx_buf; / * send buffer * / void * rx_buf / * receive buffer * / unsigned len; / * buffer length, size of tx and rx (in bytes). Refers to their respective sizes * / dma_addr_t tx_dma; / * dma address of tx * / dma_addr_t rx_dma; / * dma address of rx * / unsigned cs_change:1; / * re-slice selection after the current spi_transfer has been sent * / U8 bits_per_word / * the number of bits per word length. 0 represents the delay after the completion of a spi_transfer is sent using the default value of 8bits / u16 delay_usecs; / * in spi_device. The delay between the end of the transmission and the change in chip selection will then start another transmission or end the entire message * / U32 speed_hz; / * communication clock. If 0, use the default value of * / # ifdef CONFIG_SPI_LOMBO struct lombo_spi_operate_para * esop; # endif struct list_head transfer_list; / * to link to the spi_message, the bi-directional link node used to connect * /}

First, the transfer_list linked list field is used to hang the transfer in a spi_message structure, tx_buf and rx_buf provide the data buffer address in non-dma mode, len is the length of the data to be transmitted, and tx_dma and rx_dma give the buffer address in dma mode. In principle, spi_transfer is the smallest unit of transmission. I think the reason why spi_message is introduced for packaging is that sometimes you want to write to multiple discontiguous addresses (or registers) of spi devices at once, and if there is no spi_message to package such multiple spi_transfer, because usually the real data transfer is done in another kernel thread (work queue). The consequence of not packaging is that it will result in more process switching, lower efficiency and increased delay, especially for small-scale data transmission with multiple discontiguous addresses.

2.3.5. Spi_board_info

Struct spi_board_info {/ * the device name and module name are coupled, like platform_bus; * "modalias" is normally the driver name. * * platform_data goes to spi_device.dev.platform_data, * controller_data goes to spi_device.controller_data, * irq is copied too * / char modalias [SPI _ NAME_SIZE]; / * name * / const void * platform_data; / * platform data * / void * controller_data / * Controller data * / int irq; / * slower signaling on noisy or low voltage boards * / U32 max_speed_hz; / * maximum rate * / / * bus_num is board specific and matches the bus_num of some * spi_master that will probably be registered later. * chip_select reflects how this chip is wired to that master; * it's less than num_chipselect. * / u16 bus_num; / * spi bus number * / u16 chip_select; / * slice selection * / / * mode becomes spi_device.mode, and is essential for chips * where the default of SPI_CS_HIGH = 0 is wrong. * / U8 mode; / * mode * / / *. May need additional spi_device chip config data here. * avoid stuff protocol drivers can set; but include stuff * needed to behave without being bound to a driver: *-quirks like clock rate mattering when not selected * /}

2.4. Data transmission flow

The overall data transfer process is roughly as follows:

Hongmeng official Strategic Cooperation to build HarmonyOS Technology Community

Define a spi_message structure

Initialize spi_message with the spi_message_init function

Define one or more spi_transfer structures, initialize and prepare buffers for the data and assign values to the corresponding fields of the spi_transfer (tx_buf,rx_buf, etc.)

Hang these spi_transfer under the spi_message structure through the spi_message_init function

If you use synchronous mode, call spi_sync (), and if you use asynchronous mode, call spi_async (); (when I debug peripherals, I have only used spi_sync

The transmission diagram is shown in figure 2-5:

2.4.1. Data preparation

2.4.1.1. Spi_message_init

Static inline void spi_message_init (struct spi_message * m) {memset (m, 0, sizeof * m); INIT_LIST_HEAD (& m-> transfers);}

Initialize spi_message: clear the message and initialize the transfers chain header.

2.4.1.2. Spi_message_add_tail

Static inline void spi_message_add_tail (struct spi_transfer * t, struct spi_message * m) {list_add_tail (& t-> transfer_list, & m-> transfers);}

Add spi_transfer to the end of the linked list of spi_message.

2.4.2. Data transmission

There are two ways to transfer SPI data: synchronous mode and asynchronous mode. The so-called synchronous mode means that the initiator of data transmission must wait for the end of this transmission and cannot do anything else during this period. It is explained by code that the function will not return until the data transfer is completed after the function of transmission is called. The asynchronous mode is just the opposite. The initiator of the data transfer does not need to wait for the end of the transfer. There are other things that can be done during the data transfer. It is explained by the code that the function will return immediately after the transfer function is called without waiting for the data transfer to be completed. We just need to set a callback function, which will be called to notify the initiator that the data transfer has been completed. The synchronization method is simple and easy to use, and is very suitable for dealing with a single transmission of those small amounts of data. However, for the transmission with large amount of data and many times, the asynchronous mode is more suitable. For SPI controllers, the following two situations must be considered to support asynchronous mode:

For the initiator of the same data transfer, since the asynchronous method does not need to wait for the data transfer to be completed, the initiator can immediately initiate another message after the return, and the previous message has not yet been processed.

For a different initiator, it is also possible to initiate a message transfer request at the same time to first analyze the implementation flow of the spi_sync () interface, as shown in figure 2-6:

< 0) { printk("can't get cs-gpio"); return -EINVAL; } /* 3、设置GPIO1_IO20为输出,并且输出高电平 */ ret = gpio_direction_output(icm20608dev.cs_gpio, 1); if(ret < 0) { printk("can't set gpio!\r\n"); } /*初始化spi_device */ spi->

Mode = SPI_MODE_0; / * MODE0,CPOL=0,CPHA=0*/ spi_setup (spi); icm20608dev.private_data = spi; / * set private data * / / * initialize ICM20608 internal register * / icm20608_reginit (); return 0 } / * * @ description: remove function of spi driver. When removing spi driver, this function executes * @ param-client: spi device * @ return: 0, success; other negative values, failure * / static int icm20608_remove (struct spi_device * spi) {/ * delete device * / cdev_del (& icm20608dev.cdev); unregister_chrdev_region (icm20608dev.devid, ICM20608_CNT) / * cancel classes and devices * / device_destroy (icm20608dev.class, icm20608dev.devid); class_destroy (icm20608dev.class); return 0;} / * traditional matching method ID list * / static const struct spi_device_id icm20608_id [] = {{"alientek,icm20608", 0}, {}} / * device tree matching list * / static const struct of_device_id icm20608_of_match [] = {{.trees = "alientek,icm20608"}, {/ * Sentinel * /}} / * SPI driver structure * / static struct spi_driver icm20608_driver = {.probe = icm20608_probe, .remove = icm20608_remove, .driver = {.owner = THIS_MODULE, .name = "icm20608", .of _ match_table = icm20608_of_match,}, .id _ table = icm20608_id,} / * * @ description: driver entry function * @ param: none * @ return: no * / static int _ init icm20608_init (void) {return spi_register_driver (& icm20608_driver);} / * * @ description: driver exit function * @ param: no * @ return: no * / static void _ exit icm20608_exit (void) {spi_unregister_driver (& icm20608_driver) } module_init (icm20608_init); module_exit (icm20608_exit); MODULE_LICENSE ("GPL"); MODULE_AUTHOR (yikoulinux "); this is the answer to the question about how to drive SPI under Linux. I hope the above content can be of some help to everyone. If you still have a lot of doubts to be solved, you can follow the industry information channel for more related knowledge.

Welcome to subscribe "Shulou Technology Information " to get latest news, interesting things and hot topics in the IT industry, and controls the hottest and latest Internet news, technology news and IT industry trends.

Views: 0

*The comments in the above article only represent the author's personal views and do not represent the views and positions of this website. If you have more insights, please feel free to contribute and share.

Share To

Servers

Wechat

© 2024 shulou.com SLNews company. All rights reserved.

12
Report