z3j6w9
1
rk3288上还留有一个可以外接设备的spi0,所以我使用的是spi0,设备树文件如下,设备树中我没有指定CPOL和CPHA,在probe里手动赋值的
static int flash_spi_probe(struct spi_device *spi)
{
struct spidev_data *spidev;
struct device_node *np = spi->dev.of_node; /*Get device tree node*/
int status;
unsigned long minor;
int ret;
printk("%s \n", __FUNCTION__);
/* Allocate driver data */
spidev = kzalloc(sizeof(*spidev), GFP_KERNEL);
if (!spidev)
return -ENOMEM;
/* Initialize the driver data */
spi->mode = 1;
spi->max_speed_hz = 1000 * 1000 *10;
spi->bits_per_word = 8;
spidev->spi = spi;
/*inin lock*/
spin_lock_init(&spidev->spi_lock);
mutex_init(&spidev->buf_lock);
/*Reset register condition*/
At45db_nrst = of_get_named_gpio(np, "At45db_nrst", 0);
ret = gpio_request(At45db_nrst, "At45db_nrst");
if (ret)
return -ENODEV;
gpio_direction_output(At45db_nrst, GPIO_LOW);
gpio_direction_output(At45db_nrst, GPIO_HIGH);
/*Turn off write protect*/
At45db_nwp = of_get_named_gpio(np, "At45db_nwp", 0);
ret = gpio_request(At45db_nwp, "At45db_nwp");
if (ret)
return -ENODEV;
//At45db_flash_init(spidev);
INIT_LIST_HEAD(&spidev->device_entry);
/* If we can allocate a minor number, hook up this device.
* Reusing minors is fine so long as udev or mdev is working.
*/
mutex_lock(&device_list_lock);
minor = find_first_zero_bit(minors, N_SPI_MINORS);
if (minor < N_SPI_MINORS) {
struct device *dev;
spidev->devt = MKDEV(SPIDEV_MAJOR, minor);
dev = device_create(spiflash_cls, &spi->dev, spidev->devt,
spidev, "spidev%d.%d",
spi->master->bus_num, spi->chip_select);
status = PTR_RET(dev);
} else {
dev_dbg(&spi->dev, "no minor number available!\n");
status = -ENODEV;
}
if (status == 0) {
set_bit(minor, minors);
list_add(&spidev->device_entry, &device_list);
}
mutex_unlock(&device_list_lock);
if (status == 0)
spi_set_drvdata(spi, spidev);
else
kfree(spidev);
return status;
}
和设备树匹配的代码,基本都是这个套路,匹配compatible就行了
flash_spi.rar (3.24 KB)
AT45DB041D.rar (708 KB)
z3j6w9
2
本帖最后由 z3j6w9 于 2017-7-12 14:38 编辑
在probe中修改了max_frequence,是因为执行写操作报warning
spi_master spi0: DW SPI: Status keeps busy for 1000us after a read/write!
其实就是频率有点快,调慢点就行了
spi flash读数据前都需要写命令,刚开始先发命令再调用read获取数据,怎么都调不对波形,后来参看了下韦东山写的spi flash的代码,写成如下的形式就ok了;韦东山的spi视频是要花钱的,不过就19块,有兴趣的可以看看,我最近比较懒,所以就没去买来看了。
static inline ssize_t
spidev_sync_read(struct spidev_data *spidev, FLASH_C *flash_c)
{
struct spi_transfer t[] = {
{
.tx_buf = flash_c->cmd,
.len = flash_c->cmd_len,
},
{
.rx_buf = flash_c->read_buf,
.len = flash_c->read_buf_len,
}
};
struct spi_message m;
spi_message_init(&m);
spi_message_add_tail(&t[0], &m);
spi_message_add_tail(&t[1], &m);
return spidev_sync(spidev, &m);
}
z3j6w9
3
本帖最后由 z3j6w9 于 2017-7-12 14:48 编辑
在对spi进行操作之前需要获取下当前芯片的状态,在初始化过程中操作是没有回应的。还有就是,我一开始在初始化后就尝试获取状态,因为调用的是read函数,结果就在如下代码中挂掉了,程序状态直接变成了D+,而且不可中断,重启无数次。
static ssize_t
spidev_sync(struct spidev_data *spidev, struct spi_message *message)
{
DECLARE_COMPLETION_ONSTACK(done);
int status;
#if 1
message->complete = spidev_complete;
message->context = &done;
#endif
spin_lock_irq(&spidev->spi_lock);
if (spidev->spi == NULL)
status = -ESHUTDOWN;
else
status = spi_async(spidev->spi, message);
spin_unlock_irq(&spidev->spi_lock);
if (status == 0) {
wait_for_completion(&done);
status = message->status;
if (status == 0)
status = message->actual_length;
}
return status;
}
后续就改成了在每次读写执行获取芯片状态,不在锁里面做,防止死掉挂掉的时候会提示hung task,分享个别人写的定位的博客,其实写代码的过程中遇到问题是好事,真的能学会很多
http://blog.chinaunix.net/xmlrpc.php?r=blog/article&id=4406510&uid=14528823
z3j6w9
4
本帖最后由 z3j6w9 于 2017-7-12 15:05 编辑
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <assert.h>
typedef struct TEST_Flash_Ctrl
{ unsigned char *cmd;
unsigned char *read_buf;
unsigned char *w_data;
unsigned char cmd_len;
unsigned char read_buf_len;
unsigned char w_len;
unsigned char resv;
}TEST_C;
static int fd;
static int write_to_flash(TEST_C *buf)
{
return write(fd, buf, sizeof(TEST_C));
}
static int read_from_flash(TEST_C *buf)
{
return read(fd, buf, buf->read_buf_len);
}
static int init_flash()
{
/*erase before write*/
int ret ;
TEST_C buf;
unsigned char erase_cmd[] = {0xc7, 0x94, 0x80, 0x9a};
buf.cmd = erase_cmd;
buf.cmd_len = sizeof(erase_cmd);
ret = write_to_flash(&buf);
if (0 == ret)
{
printf("%s: write err.\n ", __FUNCTION__);
return ret;
}
return 0;
}
static int write_buffer(unsigned short addr, unsigned char* data, unsigned char len)
{
unsigned char w_cmd[4];
unsigned char addr_u = (unsigned char)(addr >> 8);
unsigned char addr_v = (unsigned char)addr;
int ret;
TEST_C buf;
w_cmd[0] = 0x84;
w_cmd[1] = 0xff;
w_cmd[2] = addr_u;
w_cmd[3] = addr_v;
buf.cmd = w_cmd;
buf.cmd_len = sizeof(w_cmd);
buf.w_data = data;
buf.w_len = len;
ret = write_to_flash(&buf);
if (0 == ret)
{
printf("%s: write err.\n ", __FUNCTION__);
return ret;
}
return 0;
}
static int read_buffer(unsigned short add, unsigned char *read_buf, unsigned char len)
{
unsigned char r_cmd[5];
unsigned char addr_u;
unsigned char addr_v;
TEST_C test_c;
TEST_C buf;
addr_u = (unsigned char)(add >> 8);
addr_v = (unsigned char)add;
r_cmd[0] = 0xd4;
r_cmd[1] = 0xff;
r_cmd[2] = addr_u;
r_cmd[3] = addr_v;
r_cmd[4] = 0xff;
buf.cmd = r_cmd;
buf.cmd_len = sizeof(r_cmd);
buf.read_buf = read_buf;
buf.read_buf_len = len;
read_from_flash(&buf);
}
static unsigned char read_status()
{
TEST_C buf;
unsigned char cmd[1];
unsigned char status = 0;
printf("Get status \n");
cmd[0] = 0xd7;
buf.cmd = cmd;
buf.cmd_len = 1;
buf.read_buf = &status;
buf.read_buf_len = 1;
read_from_flash(&buf);
return status;
}
int main(void)
{
int ret = 0;
unsigned char buf_room[10];
unsigned char status;
unsigned short addr = 0x0;
unsigned char data[] = {0x00, 0xff, 0x0, 0xff, 0x0, 0xff};
fd = open("/dev/spidev0.0", O_RDWR);
if (fd < 0)
{
printf("%s open device failed. \n", __FUNCTION__);
return -1;
}
init_flash();
#if 1
write_buffer(addr, data, sizeof(data));
read_buffer(addr, buf_room, 8);
printf("Data is %x \n", buf_room[0]);
printf("Data is %x \n", buf_room[1]);
printf("Data is %x \n", buf_room[2]);
#endif
ret = close(fd);
return 0;
}
z3j6w9
6
代码中只实现了对buffer的写/读,有兴趣研究嵌入式mcu开发的可以在论坛中私信我,大家私下一起交流技术
请问自己新增一个档案当作driver,都需要修改dts档案吗??
z3j6w9
8
aaaaa-ray 发表于 2017-7-21 11:24
请问自己新增一个档案当作driver,都需要修改dts档案吗??
dts中写的是硬件相关的信息,如果你需要用到的硬件信息设备树中已经有了,那就不需要修改设备树了。设备树的出现其实就是替代了以前驱动中的board_info注册,将通用的硬件信息给整合了起来,既方便阅读,也方便开发。
楼主,我最近入手了一块RK3328开发板,打算在预留的40pin插针上接一个spi设备,可是接线的时候发现压根就没有IRQ和RESET脚预留出来,管脚服用好像也不是太行,卡在这里了,请楼主点拨。附上原理图:
![]()
z3j6w9
13
luokaisong130 发表于 2018-3-9 15:03
楼主,我最近入手了一块RK3328开发板,打算在预留的40pin插针上接一个spi设备,可是接线的时候发现压根就没 …
irq和reset都是可以接普通GPIO的,先来说说irq,所谓的中断,其实就是监测一个电位的变化,将GPIO设置为中断模式完全可以做到,reset就更简单了,通过程序控制电平即可,一般情况下很多芯片的RESET上面都有取反符号为低电平有效,所以程序运行时设置为高电平即可。
z3j6w9 发表于 2018-3-18 10:06
irq和reset都是可以接普通GPIO的,先来说说irq,所谓的中断,其实就是监测一个电位的变化,将GPIO设置为 …
谢谢大神,我后面也是这么做的,第一次玩嵌入式是小白,哈哈
楼主,你好,我参考你的代码,在aio-3288j主板上进行测试,代码编译成功,/dev目录下也产生了设备节点;我使用的是aio-3288j主板上真实的spi2,用逻辑分析仪抓取波形,没有输出;
我初步推断,可能是At45db_nwp和At45db_nrst的gpio设置问题,这个需要怎么设置?给些指点,谢谢!
我的aio-3288j主板dts中spi2设置见图1,spi2的管脚见图2