嵌入式第六十七天(SPI子系统架构)
SPI子系统架构
SPI硬件构成
SPI(Serial Peripheral Interface)是一种同步串行通信接口,硬件上由以下信号线构成:
- SCLK(Serial Clock):主设备产生的时钟信号,用于同步数据传输。
- MOSI(Master Out Slave In):主设备输出、从设备输入的数据线。
- MISO(Master In Slave Out):从设备输出、主设备输入的数据线。
- CS(Chip Select):片选信号,由主设备控制,用于选择从设备。
- GND(Ground):共地参考,确保电平一致。
时钟极性与相位
SPI的通信模式由时钟极性(CPOL)和时钟相位(CPHA)共同决定,组合出四种工作模式:
CPOL(Clock Polarity):定义SCLK空闲时的电平状态。
CPOL=0
:SCLK空闲时为低电平,有效时从低到高跳变。CPOL=1
:SCLK空闲时为高电平,有效时从高到低跳变。
CPHA(Clock Phase):定义数据采样的边沿。
CPHA=0
:数据在SCLK的第一个边沿(上升或下降)采样。CPHA=1
:数据在SCLK的第二个边沿采样。
四种SPI模式
Mode 0:
CPOL=0
,CPHA=0
- SCLK空闲为低电平,数据在上升沿采样。
Mode 1:
CPOL=0
,CPHA=1
- SCLK空闲为低电平,数据在下降沿采样。
Mode 2:
CPOL=1
,CPHA=0
- SCLK空闲为高电平,数据在下降沿采样。
Mode 3:
CPOL=1
,CPHA=1
- SCLK空闲为高电平,数据在上升沿采样。
adxl345_drv.c
#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/i2c.h>
#include <linux/miscdevice.h>
#include <asm/uaccess.h>
#include <linux/spi/spi.h>static struct spi_device *spi_device = NULL;static int init_adxl345(void)
{int ret = 0;unsigned char txbuf[2] = {0};unsigned char rxbuf[2] = {0};txbuf[0] = 0x31 | 0x80;ret = spi_write_then_read(spi_device, txbuf, 2, rxbuf, 2);if (ret) {pr_info("spi_write_then_read failed\n");return -1;}txbuf[0] = 0x31;txbuf[1] = rxbuf[1] | 0x3;ret = spi_write_then_read(spi_device, txbuf, 2, NULL, 0);if (ret) {pr_info("spi_write_then_read failed\n");return -1;}txbuf[0] = 0x2D;txbuf[1] = 0x0B;ret = spi_write_then_read(spi_device, txbuf, 2, NULL, 0);if (ret) {pr_info("spi_write_then_read failed\n");return -1;}return 0;
}static int read_adxl345_xyz(short *pdata, int num)
{int ret = 0;unsigned char rxbuf[7];unsigned char txbuf[7];struct spi_message sendmessage;struct spi_transfer transfer = {.tx_buf = txbuf,.rx_buf = rxbuf,.len = 7,.delay_usecs = 20,};spi_message_init(&sendmessage);txbuf[0] = 0x32 | 0x80 | 0x40; spi_message_add_tail(&transfer, &sendmessage);ret = spi_sync(spi_device, &sendmessage);if (ret) {pr_info("spi_async failed\n");return -1;}pdata[0] = ((rxbuf[1] << 8) | (rxbuf[2]));pdata[1] = ((rxbuf[3] << 8) | (rxbuf[4]));pdata[2] = ((rxbuf[5] << 8) | (rxbuf[6]));return 0;
}static ssize_t adxl345_read(struct file *fp, char __user *puser, size_t n, loff_t *off)
{int ret = 0;short data[3] = {0};unsigned long nret = 0;ret = init_adxl345();if (ret) {pr_info("init_adxl345 failed\n");return -1;}ret = read_adxl345_xyz(data, 3);if (ret) {pr_info("read_adxl345_xyz failed\n");return -1;}nret = copy_to_user(puser, data, sizeof(data));if (nret) {pr_info("copy_to_user failed\n");return -1;}return 0;
}static struct file_operations fops = {.owner = THIS_MODULE,.read = adxl345_read,
};static struct miscdevice misc_adxl345 = {.minor = MISC_DYNAMIC_MINOR,.name = "misc_adxl345",.fops = &fops,
};static int adxl345_probe(struct spi_device *spi)
{int ret = 0;spi_device = spi;ret = misc_register(&misc_adxl345);if (ret) {pr_info("misc_register failed\n");return -1;}pr_info("probe ok\n");return 0;
}static int adxl345_remove(struct spi_device *spi)
{int ret = 0;ret = misc_deregister(&misc_adxl345);if (ret) {pr_info("misc_deregister failed\n");return -1;}pr_info("remove ok\n");return 0;
}static struct spi_device_id adxl345_id_table[] = {{.name = "puteadxl345"},{},
};static struct of_device_id adxl345_of_match_table[] = {{.compatible = "pute,puteadxl345"},{},
};static struct spi_driver adxl345_drv = {.probe = adxl345_probe,.remove = adxl345_remove,.driver = {.name = "puteadxl345",.owner = THIS_MODULE,.of_match_table = adxl345_of_match_table,},.id_table = adxl345_id_table,
};module_spi_driver(adxl345_drv);MODULE_LICENSE("GPL");
MODULE_AUTHOR("pute");
adxl345_app.c
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/stat.h>
#include <fcntl.h>int main(void)
{int fd = 0;short data[3] = {0};fd = open("/dev/misc_adxl345", O_RDWR);if (-1 == fd){perror("fail to open");return -1;}while (1){read(fd, data, sizeof(data));printf("x = %010d,y = %010d,z = %010d\r", data[0]);fflush(stdout);usleep(10000);}close(fd);return 0;
}