1、SPI簡(jiǎn)介
1.1 四根線
MISO:主設(shè)備數(shù)據(jù)輸入,從設(shè)備數(shù)據(jù)輸出。
MOSI:主設(shè)備數(shù)據(jù)輸出,從設(shè)備數(shù)據(jù)輸入。
SCLK:時(shí)鐘信號(hào),由主設(shè)備產(chǎn)生。
CS:? ? 從設(shè)備片選信號(hào),由主設(shè)備控制。
1.2 四種模式
CPOL(時(shí)鐘極性) :? ?0:時(shí)鐘起始位低電平 ? ? ?1:時(shí)鐘起始為高電平 ?
CPHA(時(shí)鐘相位) :0:第一個(gè)時(shí)鐘周期采樣? ?1:第二個(gè)時(shí)鐘周期采樣
1、CPOL=0,CPHA=0:此時(shí)空閑態(tài)時(shí),SCLK處于低電平,數(shù)據(jù)采樣是在第1個(gè)邊沿,也就是 SCLK由低電平到高電平的跳變,所以數(shù)據(jù)采樣是在上升沿,數(shù)據(jù)發(fā)送是在下降沿。
2、CPOL=0,CPHA=1:此時(shí)空閑態(tài)時(shí),SCLK處于低電平,數(shù)據(jù)發(fā)送是在第1個(gè)邊沿,也就是 SCLK由低電平到高電平的跳變,所以數(shù)據(jù)采樣是在下降沿,數(shù)據(jù)發(fā)送是在上升沿。
3、CPOL=1,CPHA=0:此時(shí)空閑態(tài)時(shí),SCLK處于高電平,數(shù)據(jù)采集是在第1個(gè)邊沿,也就是 SCLK由高電平到低電平的跳變,所以數(shù)據(jù)采集是在下降沿,數(shù)據(jù)發(fā)送是在上升沿。
4、CPOL=1,CPHA=1:此時(shí)空閑態(tài)時(shí),SCLK處于高電平,數(shù)據(jù)發(fā)送是在第1個(gè)邊沿,也就是 SCLK由高電平到低電平的跳變,所以數(shù)據(jù)采集是在上升沿,數(shù)據(jù)發(fā)送是在下降沿。
1.3 SPI數(shù)據(jù)傳輸
????????
????????從圖中可以看出,主機(jī)和從機(jī)都有一個(gè)串行移位寄存器,主機(jī)通過(guò)向它的SPI串行寄存器寫入一個(gè)字節(jié)來(lái)發(fā)起一次傳輸。寄存器通過(guò)MOSI信號(hào)線將字節(jié)傳送給從機(jī),從機(jī)也將自己的移位寄存器中的內(nèi)容通過(guò)MISO信號(hào)線返回給主機(jī)。這樣,兩個(gè)移位寄存器中的內(nèi)容就被交換。外設(shè)的寫操作和讀操作是同步完成的。如果只進(jìn)行寫操作,主機(jī)只需忽略接收到的字節(jié);反之,若主機(jī)要讀取從機(jī)的一個(gè)字節(jié),就必須發(fā)送一個(gè)空字節(jié)來(lái)引發(fā)從機(jī)的傳輸。
1.4?SPI驅(qū)動(dòng)框架簡(jiǎn)介
????????SPI 驅(qū)動(dòng)框架和 I2C 很類似,都分為主機(jī)控制器驅(qū)動(dòng)和設(shè)備驅(qū)動(dòng),主機(jī)控制器也就是 SOC 的 SPI 控制器接口。樣。和 I2C適配器驅(qū)動(dòng)一樣,SPI主機(jī)驅(qū)動(dòng)一般都是 SOC 廠商去編寫的,所以我們作為 SOC的使用者,這一部分的驅(qū)動(dòng)就不用操心了。
2、SPI設(shè)備驅(qū)動(dòng)
2.1 SPI相關(guān)API
2.1.1 spi_driver
struct spi_driver {
int (*probe)(struct spi_device *spi);
int (*remove)(struct spi_device *spi);
struct device_driver driver;
};
struct device_driver {
const char *name;
const struct of_device_id *of_match_table;
}
2.1.2 注冊(cè):spi_register_driver
#define spi_register_driver(driver)
__spi_register_driver(THIS_MODULE, driver)
2.1.3 注銷:spi_unregister_driver
static inline void spi_unregister_driver(struct spi_driver *sdrv)
2.1.4 module_spi_driver:一鍵注冊(cè),不需要以上注冊(cè)注銷的過(guò)程
#define module_spi_driver(__spi_driver)
module_driver(__spi_driver, spi_register_driver,
spi_unregister_driver)
#define module_driver(__driver, __register, __unregister, ...)
static int __init __driver##_init(void)
{
return __register(&(__driver) , ##__VA_ARGS__);
}
module_init(__driver##_init);
static void __exit __driver##_exit(void)
{
__unregister(&(__driver) , ##__VA_ARGS__);
}
module_exit(__driver##_exit);
module_spi_driver(myspi);
#define module_spi_driver(myspi)
module_driver(myspi, spi_register_driver,
spi_unregister_driver)
#define module_driver(myspi, spi_register_driver, spi_unregister_driver)
static int __init myspi_init(void)
{
return spi_register_driver(&myspi);
}
static void __exit myspi_exit(void)
{
spi_unregister_driver(&myspi);
}
module_init(myspi_init);
module_exit(myspi_exit);
2.1.5 spi_write
spi_write(struct spi_device *spi, const void *buf, size_t len)
{
struct spi_transfer t = {
.tx_buf = buf,
.len = len,
};
return spi_sync_transfer(spi, &t, 1);
}
2.1.6 spi_read
spi_read(struct spi_device *spi, void *buf, size_t len)
{
struct spi_transfer t = {
.rx_buf = buf,
.len = len,
};
return spi_sync_transfer(spi, &t, 1);
}
2.1.7 spi_write_then_read?
extern int spi_write_then_read(struct spi_device *spi,
const void *txbuf, unsigned n_tx,
void *rxbuf, unsigned n_rx);
2.1.8 以上三種spi傳輸函數(shù)解析,實(shí)際接收發(fā)送只需spi_write、spi_read
struct spi_transfer {
const void *tx_buf; //用于保存發(fā)送的數(shù)據(jù)
void *rx_buf; //用于保存接收到的數(shù)據(jù)
unsigned len; //是要進(jìn)行傳輸?shù)臄?shù)據(jù)長(zhǎng)度, SPI是全雙工通信,
//因此在一次通信中發(fā)送和接收的字節(jié)數(shù)都是一樣的,
//所以 spi_transfer中也就沒(méi)有發(fā)送長(zhǎng)度和接收長(zhǎng)度之分。
};
spi_sync_transfer(struct spi_device *spi, struct spi_transfer *xfers,
unsigned int num_xfers)
{
struct spi_message msg;
spi_message_init_with_transfers(&msg, xfers, num_xfers);
return spi_sync(spi, &msg);
}
struct spi_message {
struct list_head transfers;
struct spi_device *spi;
};
spi_message_init_with_transfers(struct spi_message *m,
struct spi_transfer *xfers, unsigned int num_xfers)
{
unsigned int i;
spi_message_init(m);
for (i = 0; i < num_xfers; ++i)
spi_message_add_tail(&xfers[i], m);
}
總體流程:文章來(lái)源:http://www.zghlxwxcb.cn/news/detail-461604.html
①、申請(qǐng)并初 始化 spi_transfer,設(shè)置 spi_transfer的 tx_buf成員變量, tx_buf為要發(fā)送的數(shù)
據(jù)。然后設(shè)置 rx_buf成員變量, rx_buf保存著接收到的數(shù)據(jù)。最后設(shè)置 len成員變量,也就是要進(jìn)行數(shù)據(jù)通信的長(zhǎng)度。
②、使用 spi_message_init函數(shù)初始化 spi_message。
③、使用 spi_message_add_tail函數(shù)將前面設(shè)置好的 spi_transfer添加到 spi_message隊(duì)列中。
④、使用 spi_sync函數(shù)完成 SPI數(shù)據(jù)同步傳輸。文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-461604.html
?2.1.9?spi_message_init
int spi_sync(struct spi_device *spi, struct spi_message *message)
/*
功能:初始化spi_message
參數(shù):
@spi :要進(jìn)行數(shù)據(jù)傳輸?shù)?spi_device
@message:要傳輸?shù)?spi_message
返回值:無(wú)
*/
2.1.10?spi_message_add_tail
void spi_message_add_tail(struct spi_transfer *t, struct spi_message *m)
/*
功能:初始化完成以后需要將 spi_transfer添加到 spi_message隊(duì)列中
參數(shù):
@t:要添加到隊(duì)列中的 spi_transfer
@m:spi_transfer要加入的 spi_message
返回值: 無(wú)
*/
2.1.11?spi_sync:同步傳輸
int spi_sync(struct spi_device *spi, struct spi_message *message)
/*
功能:同步傳輸,會(huì)阻塞的等待 SPI 數(shù)據(jù)傳輸完成
參數(shù):
@spi:要進(jìn)行數(shù)據(jù)傳輸?shù)?spi_device
@message:要傳輸?shù)?spi_message
返回值:成功返回0,失敗返回錯(cuò)誤碼
*/
2.1.12?spi_async:異步傳輸
int spi_async(struct spi_device *spi, struct spi_message *message)
/*
功能:異步傳輸不會(huì)阻塞的等到 SPI數(shù)據(jù)傳輸完成,異步傳輸需要設(shè)置 spi_message中的 complete成員變量,
complete是一個(gè)回調(diào)函數(shù),當(dāng) SPI異步傳輸完成以后此函數(shù)就會(huì)被調(diào)用。
參數(shù):
@spi:要進(jìn)行數(shù)據(jù)傳輸?shù)?spi_device
@message:要傳輸?shù)?spi_message
返回值:成功返回0,失敗返回錯(cuò)誤碼
*/
3、驅(qū)動(dòng)程序
3.1 修改設(shè)備樹(shù)
&spi4{
pinctrl-names = "default", "sleep";
pinctrl-0 = <&spi4_pins_b>;
pinctrl-1 = <&spi4_sleep_pins_b>;
cs-gpios = <&gpioe 11 0>;
status = "okay";
m74hc595@0{
compatible = "aaa,m74hc595";
reg = <0>;
spi-max-frequency = <10000000>; //10Mhz
};
};
3.2?驅(qū)動(dòng)程序編寫
#ifndef __M74HC595_H__
#define __M74HC595_H__
#define SEG_WHICH _IOW('k',0,int)
#define SEG_DAT _IOW('k',1,int)
#endif
#define NAME "m74hc595"
int major = 0;
struct class *cls;
struct device *dev;
struct spi_device *gspi;
u8 code[] = {
0x3f, 0x06, 0x5b, 0x4f, 0x6d, 0x7d, 0x07, 0x7f,
0x6f, 0x77, 0x7c, 0x39, 0x5e, 0x79, 0x71,
};
u8 which[] = {
0x1, 0x2, 0x4, 0x8,
};
int m74hc595_open(struct inode *inode, struct file *file)
{
return 0;
}
long m74hc595_ioctl(struct file *file,
unsigned int cmd, unsigned long args)
{
switch(cmd){
case SEG_WHICH:
spi_write(gspi,&which[args],1);
break;
case SEG_DAT:
spi_write(gspi,&code[args],1);
break;
default: printk("ioctl error\n");break;
}
return 0;
}
int m74hc595_close(struct inode *inode, struct file *file)
{
return 0;
}
struct file_operations fops = {
.open = m74hc595_open,
.unlocked_ioctl = m74hc595_ioctl,
.release = m74hc595_close,
};
int m74hc595_probe(struct spi_device *spi)
{
u8 buf[2] = {0xf,0x0};
gspi = spi;
spi_write(gspi,buf,ARRAY_SIZE(buf));
major = register_chrdev(0,NAME,&fops);
cls = class_create(THIS_MODULE,NAME);
dev = device_create(cls,NULL,MKDEV(major,0),NULL,NAME);
return 0;
}
int m74hc595_remove(struct spi_device *spi)
{
device_destroy(cls,MKDEV(major,0));
class_destroy(cls);
unregister_chrdev(major,NAME);
return 0;
}
const struct of_device_id of_match[] = {
{.compatible = "aaa,m74hc595",},
{},
};
struct spi_driver m74hc595 = {
.probe = m74hc595_probe,
.remove = m74hc595_remove,
.driver = {
.name = "bbb",
.of_match_table = of_match,
},
};
module_spi_driver(m74hc595);
4、應(yīng)用程序 ?
int main(int argc, const char *argv[])
{
int which=0;
int data=0;
int fd;
fd = open("/dev/m74hc595",O_RDWR);
if(fd < 0){
perror("open error");
return -1;
}
while(1){
ioctl(fd,SEG_WHICH,which++);
ioctl(fd,SEG_DAT,data++);
if(which >= 4)which=0;
if(data >= 16)data = 0;
sleep(1);
}
close(fd);
return 0;
}
到了這里,關(guān)于Linux驅(qū)動(dòng)開(kāi)發(fā):SPI子系統(tǒng)的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!