mirror of
https://e.coding.net/weidongshan/linux/doc_and_source_for_drivers.git
synced 2025-11-30 20:11:03 +08:00
发布SPI: 12_编写SPI_DAC模块驱动程序,13_编写DAC驱动_上机实验_IMX6ULL,13_编写DAC驱动_上机实验_STM32MP157
This commit is contained in:
@@ -4,12 +4,25 @@
|
||||
|
||||
* DAC芯片手册:`TLC5615.pdf`
|
||||
|
||||
|
||||
|
||||
|
||||
## 1. 硬件
|
||||
|
||||
### 1.1 原理图
|
||||
## 1. 要做什么事情
|
||||
|
||||

|
||||
|
||||
|
||||
|
||||
* 查看原理图,编写设备树
|
||||
* 编写驱动程序,注册一个spidrv
|
||||
* 编写测试程序
|
||||
|
||||
|
||||
|
||||
|
||||
## 2. 硬件
|
||||
|
||||
### 2.1 原理图
|
||||
|
||||
IMX6ULL:
|
||||
|
||||
@@ -27,9 +40,9 @@ STM32MP157:
|
||||
|
||||
|
||||
|
||||
### 1.2 连接
|
||||
### 2.2 连接
|
||||
|
||||
#### 1.2.1 IMX6ULL
|
||||
#### 2.2.1 IMX6ULL
|
||||
|
||||
DAC模块接到IMX6ULL扩展板的SPI_A插座上:
|
||||
|
||||
@@ -37,11 +50,11 @@ DAC模块接到IMX6ULL扩展板的SPI_A插座上:
|
||||
|
||||
|
||||
|
||||
#### 1.2.2 STM32MP157
|
||||
#### 2.2.2 STM32MP157
|
||||
|
||||
|
||||
|
||||
## 2. 编写设备树
|
||||
## 3. 编写设备树
|
||||
|
||||
确认SPI时钟最大频率:
|
||||
|
||||
@@ -66,7 +79,7 @@ F = 20000000 = 20MHz
|
||||
|
||||
|
||||
|
||||
### 2.1 IMX6ULL
|
||||
### 3.1 IMX6ULL
|
||||
|
||||

|
||||
|
||||
@@ -95,7 +108,7 @@ DAC模块接在这个插座上,那么要在设备树里spi1的节点下创建
|
||||
|
||||
|
||||
|
||||
### 2.2 STM32MP157
|
||||
### 3.2 STM32MP157
|
||||
|
||||

|
||||
|
||||
@@ -129,7 +142,7 @@ DAC模块接在这个插座上,那么要在设备树里spi5的节点下创建
|
||||
|
||||
|
||||
|
||||
## 3. 编写驱动程序
|
||||
## 4. 编写驱动程序
|
||||
|
||||
以前我们基于spidev编写过DAC的应用程序,可以参考它:
|
||||
|
||||
@@ -139,5 +152,5 @@ DAC模块接在这个插座上,那么要在设备树里spi5的节点下创建
|
||||
|
||||
|
||||
|
||||
## 4. 编写测试程序
|
||||
## 5. 编写测试程序
|
||||
|
||||
|
||||
Binary file not shown.
286
IMX6ULL/doc_pic/11_SPI/13_编写DAC驱动_上机实验.md
Normal file
286
IMX6ULL/doc_pic/11_SPI/13_编写DAC驱动_上机实验.md
Normal file
@@ -0,0 +1,286 @@
|
||||
# 编写DAC驱动_上机实验 #
|
||||
|
||||
* 源码
|
||||
|
||||

|
||||
|
||||
|
||||
|
||||
## 1. 硬件
|
||||
|
||||
### 1.1 原理图
|
||||
|
||||
IMX6ULL:
|
||||
|
||||

|
||||
|
||||
STM32MP157:
|
||||
|
||||

|
||||
|
||||
|
||||
|
||||
原理图:
|
||||
|
||||

|
||||
|
||||
|
||||
|
||||
### 1.2 连接
|
||||
|
||||
#### 1.2.1 IMX6ULL
|
||||
|
||||
DAC模块接到IMX6ULL扩展板的SPI_A插座上:
|
||||
|
||||

|
||||
|
||||
|
||||
|
||||
#### 1.2.2 STM32MP157
|
||||
|
||||
DAC模块接到STM32MP157扩展板的SPI_A插座上:
|
||||
|
||||

|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## 2. 编写设备树
|
||||
|
||||
确认SPI时钟最大频率:
|
||||
|
||||

|
||||
|
||||
```shell
|
||||
T = 25 + 25 = 50ns
|
||||
F = 20000000 = 20MHz
|
||||
```
|
||||
|
||||
|
||||
|
||||
设备树如下:
|
||||
|
||||
```shell
|
||||
dac: dac {
|
||||
compatible = "100ask,dac";
|
||||
reg = <0>;
|
||||
spi-max-frequency = <20000000>;
|
||||
};
|
||||
```
|
||||
|
||||
|
||||
|
||||
### 2.1 IMX6ULL
|
||||
|
||||

|
||||
|
||||
DAC模块接在这个插座上,那么要在设备树里spi1的节点下创建子节点。
|
||||
|
||||
代码在`arch/arm/boot/dts/100ask_imx6ull-14x14.dts`中,如下:
|
||||
|
||||
```shell
|
||||
&ecspi1 {
|
||||
pinctrl-names = "default";
|
||||
pinctrl-0 = <&pinctrl_ecspi1>;
|
||||
|
||||
fsl,spi-num-chipselects = <2>;
|
||||
cs-gpios = <&gpio4 26 GPIO_ACTIVE_LOW>, <&gpio4 24 GPIO_ACTIVE_LOW>;
|
||||
status = "okay";
|
||||
|
||||
dac: dac {
|
||||
compatible = "100ask,dac";
|
||||
reg = <0>;
|
||||
spi-max-frequency = <20000000>;
|
||||
};
|
||||
};
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
### 2.2 STM32MP157
|
||||
|
||||

|
||||
|
||||
DAC模块接在这个插座上,那么要在设备树里spi5的节点下创建子节点。
|
||||
|
||||
代码在`arch/arm/boot/dts/stm32mp157c-100ask-512d-lcd-v1.dts`中,如下:
|
||||
|
||||
```shell
|
||||
&spi5 {
|
||||
pinctrl-names = "default", "sleep";
|
||||
pinctrl-0 = <&spi5_pins_a>;
|
||||
pinctrl-1 = <&spi5_sleep_pins_a>;
|
||||
status = "okay";
|
||||
cs-gpios = <&gpioh 5 GPIO_ACTIVE_LOW>, <&gpioz 4 GPIO_ACTIVE_LOW>;
|
||||
spidev: icm20608@0{
|
||||
compatible = "invensense,icm20608";
|
||||
interrupts = <0 IRQ_TYPE_EDGE_FALLING>;
|
||||
interrupt-parent = <&gpioz>;
|
||||
spi-max-frequency = <8000000>;
|
||||
reg = <0>;
|
||||
};
|
||||
dac_test: dac_test@1{
|
||||
compatible = "100ask,dac";
|
||||
spi-max-frequency = <20000000>;
|
||||
reg = <1>;
|
||||
};
|
||||
};
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## 3. 编译替换设备树
|
||||
|
||||
### 3.1 IMX6ULL
|
||||
|
||||
#### 3.1.1 设置工具链
|
||||
|
||||
```shell
|
||||
export ARCH=arm
|
||||
export CROSS_COMPILE=arm-buildroot-linux-gnueabihf-
|
||||
export PATH=$PATH:/home/book/100ask_imx6ull-sdk/ToolChain/arm-buildroot-linux-gnueabihf_sdk-buildroot/bin
|
||||
```
|
||||
|
||||
|
||||
#### 3.1.2 编译、替换设备树
|
||||
|
||||
* 编译设备树:
|
||||
在Ubuntu的IMX6ULL内核目录下执行如下命令,
|
||||
得到设备树文件:`arch/arm/boot/dts/100ask_imx6ull-14x14.dtb`
|
||||
|
||||
```shell
|
||||
make dtbs
|
||||
```
|
||||
|
||||
* 复制到NFS目录:
|
||||
|
||||
```shell
|
||||
$ cp arch/arm/boot/dts/100ask_imx6ull-14x14.dtb ~/nfs_rootfs/
|
||||
```
|
||||
|
||||
* 开发板上挂载NFS文件系统
|
||||
|
||||
```shell
|
||||
[root@100ask:~]# mount -t nfs -o nolock,vers=3 192.168.1.137:/home/book/nfs_rootfs /mnt
|
||||
```
|
||||
|
||||
* 更新设备树
|
||||
|
||||
```shell
|
||||
[root@100ask:~]# cp /mnt/100ask_imx6ull-14x14.dtb /boot
|
||||
[root@100ask:~]# sync
|
||||
```
|
||||
|
||||
* 重启开发板
|
||||
|
||||
|
||||
|
||||
### 3.2 STM32MP157
|
||||
|
||||
#### 3.2.1 设置工具链
|
||||
|
||||
```shell
|
||||
export ARCH=arm
|
||||
export CROSS_COMPILE=arm-buildroot-linux-gnueabihf-
|
||||
export PATH=$PATH:/home/book/100ask_stm32mp157_pro-sdk/ToolChain/arm-buildroot-linux-gnueabihf_sdk-buildroot/bin
|
||||
```
|
||||
|
||||
|
||||
#### 3.2.2 编译、替换设备树
|
||||
|
||||
* 编译设备树:
|
||||
在Ubuntu的STM32MP157内核目录下执行如下命令,
|
||||
得到设备树文件:`arch/arm/boot/dts/stm32mp157c-100ask-512d-lcd-v1.dtb`
|
||||
|
||||
```shell
|
||||
make dtbs
|
||||
```
|
||||
|
||||
* 复制到NFS目录:
|
||||
|
||||
```shell
|
||||
$ cp arch/arm/boot/dts/stm32mp157c-100ask-512d-lcd-v1.dtb ~/nfs_rootfs/
|
||||
```
|
||||
|
||||
* 开发板上挂载NFS文件系统
|
||||
|
||||
```shell
|
||||
[root@100ask:~]# mount -t nfs -o nolock,vers=3 192.168.1.137:/home/book/nfs_rootfs /mnt
|
||||
```
|
||||
|
||||
* 确定设备树分区挂载在哪里
|
||||
|
||||
由于版本变化,STM32MP157单板上烧录的系统可能有细微差别。
|
||||
在开发板上执行`cat /proc/mounts`后,可以得到两种结果(见下图):
|
||||
|
||||
* mmcblk2p2分区挂载在/boot目录下(下图左边):无需特殊操作,下面把文件复制到/boot目录即可
|
||||
|
||||
* mmcblk2p2挂载在/mnt目录下(下图右边)
|
||||
|
||||
* 在视频里、后面文档里,都是更新/boot目录下的文件,所以要先执行以下命令重新挂载:
|
||||
* `mount /dev/mmcblk2p2 /boot`
|
||||
|
||||

|
||||
|
||||
* 更新设备树
|
||||
|
||||
```shell
|
||||
[root@100ask:~]# cp /mnt/stm32mp157c-100ask-512d-lcd-v1.dtb /boot/
|
||||
[root@100ask:~]# sync
|
||||
```
|
||||
|
||||
* 重启开发板
|
||||
|
||||
|
||||
|
||||
## 4. 编译DAC驱动
|
||||
|
||||
|
||||
|
||||
## 5. 编译APP
|
||||
|
||||
```shell
|
||||
arm-buildroot-linux-gnueabihf-gcc -o dac_test dac_test.c
|
||||
```
|
||||
|
||||
|
||||
|
||||
## 6. 上机实验
|
||||
|
||||
如果spidev没有被编译进内核,那么先执行:
|
||||
|
||||
```shell
|
||||
insmod dac_drv.ko
|
||||
```
|
||||
|
||||
|
||||
|
||||
确定设备节点:
|
||||
|
||||
```shell
|
||||
ls /dev/100ask_dac
|
||||
```
|
||||
|
||||
|
||||
|
||||
假设设备节点为`/dev/100ask_dac`,执行测试程序:
|
||||
|
||||
```shell
|
||||
./dac_test /dev/100ask_dac 500
|
||||
./dac_test /dev/100ask_dac 600
|
||||
./dac_test /dev/100ask_dac 1000
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
BIN
IMX6ULL/doc_pic/11_SPI/13_编写DAC驱动_上机实验.tif
Normal file
BIN
IMX6ULL/doc_pic/11_SPI/13_编写DAC驱动_上机实验.tif
Normal file
Binary file not shown.
BIN
IMX6ULL/doc_pic/11_SPI/14_编写SPI_OLED模块驱动程序.tif
Normal file
BIN
IMX6ULL/doc_pic/11_SPI/14_编写SPI_OLED模块驱动程序.tif
Normal file
Binary file not shown.
BIN
IMX6ULL/doc_pic/11_SPI/pic/73_src_dac_drv.png
Normal file
BIN
IMX6ULL/doc_pic/11_SPI/pic/73_src_dac_drv.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 16 KiB |
22
IMX6ULL/source/11_SPI/05_dac_use_mydrv/Makefile
Normal file
22
IMX6ULL/source/11_SPI/05_dac_use_mydrv/Makefile
Normal file
@@ -0,0 +1,22 @@
|
||||
|
||||
# 1. 使用不同的开发板内核时, 一定要修改KERN_DIR
|
||||
# 2. KERN_DIR中的内核要事先配置、编译, 为了能编译内核, 要先设置下列环境变量:
|
||||
# 2.1 ARCH, 比如: export ARCH=arm64
|
||||
# 2.2 CROSS_COMPILE, 比如: export CROSS_COMPILE=aarch64-linux-gnu-
|
||||
# 2.3 PATH, 比如: export PATH=$PATH:/home/book/100ask_roc-rk3399-pc/ToolChain-6.3.1/gcc-linaro-6.3.1-2017.05-x86_64_aarch64-linux-gnu/bin
|
||||
# 注意: 不同的开发板不同的编译器上述3个环境变量不一定相同,
|
||||
# 请参考各开发板的高级用户使用手册
|
||||
|
||||
KERN_DIR = /home/book/100ask_imx6ull-sdk/Linux-4.9.88
|
||||
|
||||
all:
|
||||
make -C $(KERN_DIR) M=`pwd` modules
|
||||
$(CROSS_COMPILE)gcc -o dac_test dac_test.c
|
||||
|
||||
clean:
|
||||
make -C $(KERN_DIR) M=`pwd` modules clean
|
||||
rm -rf modules.order dac_test
|
||||
|
||||
obj-m += dac_drv.o
|
||||
|
||||
|
||||
174
IMX6ULL/source/11_SPI/05_dac_use_mydrv/dac_drv.c
Normal file
174
IMX6ULL/source/11_SPI/05_dac_use_mydrv/dac_drv.c
Normal file
@@ -0,0 +1,174 @@
|
||||
/*
|
||||
* Simple synchronous userspace interface to SPI devices
|
||||
*
|
||||
* Copyright (C) 2006 SWAPP
|
||||
* Andrea Paterniani <a.paterniani@swapp-eng.it>
|
||||
* Copyright (C) 2007 David Brownell (simplification, cleanup)
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#include <linux/init.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/ioctl.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/list.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/compat.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/acpi.h>
|
||||
|
||||
#include <linux/spi/spi.h>
|
||||
#include <linux/spi/spidev.h>
|
||||
|
||||
#include <linux/uaccess.h>
|
||||
|
||||
#define SPI_IOC_WR 123
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
static struct spi_device *dac;
|
||||
static int major;
|
||||
|
||||
static long
|
||||
spidev_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
|
||||
{
|
||||
int val;
|
||||
int err;
|
||||
int old_val = 0;
|
||||
|
||||
struct spi_message msg;
|
||||
struct spi_transfer xfer;
|
||||
int status;
|
||||
|
||||
/* copy_from_user */
|
||||
err = copy_from_user(&val, (const void __user *)arg, sizeof(int));
|
||||
|
||||
/* 发起SPI传输: */
|
||||
|
||||
/* 1. 把val修改为正确的格式 */
|
||||
val <<= 2; /* bit0,bit1 = 0b00 */
|
||||
val &= 0xFFC; /* 只保留10bit */
|
||||
|
||||
/* 2. 发起SPI传输同时写\读 */
|
||||
/* 2.1 构造transfer
|
||||
* 2.2 加入message
|
||||
* 2.3 调用spi_sync
|
||||
*/
|
||||
xfer[0].tx_buf = &val;
|
||||
xfer[0].rx_buf = &old_val;
|
||||
xfer[0].len = 2;
|
||||
|
||||
spi_message_init(&msg);
|
||||
spi_message_add_tail(&[0], &msg);
|
||||
|
||||
status = spi_sync(dac, &msg);
|
||||
|
||||
/* 3. 修改读到的数据的格式 */
|
||||
old_val >>= 2;
|
||||
|
||||
/* copy_to_user */
|
||||
err = copy_to_user((void __user *)arg, &old_val, sizeof(int));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static const struct file_operations spidev_fops = {
|
||||
.owner = THIS_MODULE,
|
||||
/* REVISIT switch to aio primitives, so that userspace
|
||||
* gets more complete API coverage. It'll simplify things
|
||||
* too, except for the locking.
|
||||
*/
|
||||
.unlocked_ioctl = spidev_ioctl,
|
||||
};
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
/* The main reason to have this class is to make mdev/udev create the
|
||||
* /dev/spidevB.C character device nodes exposing our userspace API.
|
||||
* It also simplifies memory management.
|
||||
*/
|
||||
|
||||
static struct class *spidev_class;
|
||||
|
||||
static const struct of_device_id spidev_dt_ids[] = {
|
||||
{ .compatible = "100ask,dac" },
|
||||
{},
|
||||
};
|
||||
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
static int spidev_probe(struct spi_device *spi)
|
||||
{
|
||||
/* 1. 记录spi_device */
|
||||
dac = spi;
|
||||
|
||||
/* 2. 注册字符设备 */
|
||||
major = register_chrdev(0, "100ask_dac", &spidev_fops);
|
||||
spidev_class = class_create(THIS_MODULE, "100ask_dac");
|
||||
device_create(spidev_class, NULL, MKDEV(major, 0), NULL, "100ask_dac");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int spidev_remove(struct spi_device *spi)
|
||||
{
|
||||
/* 反注册字符设备 */
|
||||
device_destroy(spidev_class, MKDEV(major, 0));
|
||||
class_destroy(spidev_class);
|
||||
unregister_chrdev(major, "100ask_dac");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct spi_driver spidev_spi_driver = {
|
||||
.driver = {
|
||||
.name = "100ask_spi_dac_drv",
|
||||
.of_match_table = of_match_ptr(spidev_dt_ids),
|
||||
},
|
||||
.probe = spidev_probe,
|
||||
.remove = spidev_remove,
|
||||
|
||||
/* NOTE: suspend/resume methods are not necessary here.
|
||||
* We don't do anything except pass the requests to/from
|
||||
* the underlying controller. The refrigerator handles
|
||||
* most issues; the controller driver handles the rest.
|
||||
*/
|
||||
};
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
static int __init spidev_init(void)
|
||||
{
|
||||
int status;
|
||||
|
||||
status = spi_register_driver(&spidev_spi_driver);
|
||||
if (status < 0) {
|
||||
}
|
||||
return status;
|
||||
}
|
||||
module_init(spidev_init);
|
||||
|
||||
static void __exit spidev_exit(void)
|
||||
{
|
||||
spi_unregister_driver(&spidev_spi_driver);
|
||||
}
|
||||
module_exit(spidev_exit);
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
56
IMX6ULL/source/11_SPI/05_dac_use_mydrv/dac_test.c
Normal file
56
IMX6ULL/source/11_SPI/05_dac_use_mydrv/dac_test.c
Normal file
@@ -0,0 +1,56 @@
|
||||
|
||||
|
||||
/* 参考: tools\spi\spidev_fdx.c */
|
||||
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
#include <stdlib.h>
|
||||
#include <fcntl.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include <linux/types.h>
|
||||
|
||||
#define SPI_IOC_WR 123
|
||||
|
||||
/* dac_test /dev/100ask_dac <val> */
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
int fd;
|
||||
unsigned int val;
|
||||
int status;
|
||||
|
||||
unsigned char tx_buf[2];
|
||||
unsigned char rx_buf[2];
|
||||
|
||||
if (argc != 3)
|
||||
{
|
||||
printf("Usage: %s /dev/100ask_dac <val>\n", argv[0]);
|
||||
return 0;
|
||||
}
|
||||
|
||||
fd = open(argv[1], O_RDWR);
|
||||
if (fd < 0) {
|
||||
printf("can not open %s\n", argv[1]);
|
||||
return 1;
|
||||
}
|
||||
|
||||
val = strtoul(argv[2], NULL, 0);
|
||||
|
||||
status = ioctl(fd, SPI_IOC_WR, &val);
|
||||
if (status < 0) {
|
||||
printf("SPI_IOC_WR\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* 打印 */
|
||||
printf("Pre val = %d\n", val);
|
||||
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
22
IMX6ULL/source/11_SPI/06_dac_use_mydrv_ok/Makefile
Normal file
22
IMX6ULL/source/11_SPI/06_dac_use_mydrv_ok/Makefile
Normal file
@@ -0,0 +1,22 @@
|
||||
|
||||
# 1. 使用不同的开发板内核时, 一定要修改KERN_DIR
|
||||
# 2. KERN_DIR中的内核要事先配置、编译, 为了能编译内核, 要先设置下列环境变量:
|
||||
# 2.1 ARCH, 比如: export ARCH=arm64
|
||||
# 2.2 CROSS_COMPILE, 比如: export CROSS_COMPILE=aarch64-linux-gnu-
|
||||
# 2.3 PATH, 比如: export PATH=$PATH:/home/book/100ask_roc-rk3399-pc/ToolChain-6.3.1/gcc-linaro-6.3.1-2017.05-x86_64_aarch64-linux-gnu/bin
|
||||
# 注意: 不同的开发板不同的编译器上述3个环境变量不一定相同,
|
||||
# 请参考各开发板的高级用户使用手册
|
||||
|
||||
KERN_DIR = /home/book/100ask_imx6ull-sdk/Linux-4.9.88
|
||||
|
||||
all:
|
||||
make -C $(KERN_DIR) M=`pwd` modules
|
||||
$(CROSS_COMPILE)gcc -o dac_test dac_test.c
|
||||
|
||||
clean:
|
||||
make -C $(KERN_DIR) M=`pwd` modules clean
|
||||
rm -rf modules.order dac_test
|
||||
|
||||
obj-m += dac_drv.o
|
||||
|
||||
|
||||
186
IMX6ULL/source/11_SPI/06_dac_use_mydrv_ok/dac_drv.c
Normal file
186
IMX6ULL/source/11_SPI/06_dac_use_mydrv_ok/dac_drv.c
Normal file
@@ -0,0 +1,186 @@
|
||||
/*
|
||||
* Simple synchronous userspace interface to SPI devices
|
||||
*
|
||||
* Copyright (C) 2006 SWAPP
|
||||
* Andrea Paterniani <a.paterniani@swapp-eng.it>
|
||||
* Copyright (C) 2007 David Brownell (simplification, cleanup)
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#include <linux/init.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/ioctl.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/list.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/compat.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/acpi.h>
|
||||
|
||||
#include <linux/spi/spi.h>
|
||||
#include <linux/spi/spidev.h>
|
||||
|
||||
#include <linux/uaccess.h>
|
||||
|
||||
#define SPI_IOC_WR 123
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
static struct spi_device *dac;
|
||||
static int major;
|
||||
|
||||
static long
|
||||
spidev_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
|
||||
{
|
||||
int val;
|
||||
int err;
|
||||
unsigned char tx_buf[2];
|
||||
unsigned char rx_buf[2];
|
||||
|
||||
struct spi_message msg;
|
||||
struct spi_transfer xfer[1];
|
||||
int status;
|
||||
|
||||
memset(&xfer[0], 0, sizeof(xfer));
|
||||
|
||||
/* copy_from_user */
|
||||
err = copy_from_user(&val, (const void __user *)arg, sizeof(int));
|
||||
|
||||
printk("spidev_ioctl get val from user: %d\n", val);
|
||||
|
||||
/* 发起SPI传输: */
|
||||
|
||||
/* 1. 把val修改为正确的格式 */
|
||||
val <<= 2; /* bit0,bit1 = 0b00 */
|
||||
val &= 0xFFC; /* 只保留10bit */
|
||||
|
||||
tx_buf[1] = val & 0xff;
|
||||
tx_buf[0] = (val>>8) & 0xff;
|
||||
|
||||
/* 2. 发起SPI传输同时写\读 */
|
||||
/* 2.1 构造transfer
|
||||
* 2.2 加入message
|
||||
* 2.3 调用spi_sync
|
||||
*/
|
||||
xfer[0].tx_buf = tx_buf;
|
||||
xfer[0].rx_buf = rx_buf;
|
||||
xfer[0].len = 2;
|
||||
|
||||
spi_message_init(&msg);
|
||||
spi_message_add_tail(&xfer[0], &msg);
|
||||
|
||||
status = spi_sync(dac, &msg);
|
||||
|
||||
/* 3. 修改读到的数据的格式 */
|
||||
val = (rx_buf[0] << 8) | (rx_buf[1]);
|
||||
val >>= 2;
|
||||
|
||||
/* copy_to_user */
|
||||
err = copy_to_user((void __user *)arg, &val, sizeof(int));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static const struct file_operations spidev_fops = {
|
||||
.owner = THIS_MODULE,
|
||||
/* REVISIT switch to aio primitives, so that userspace
|
||||
* gets more complete API coverage. It'll simplify things
|
||||
* too, except for the locking.
|
||||
*/
|
||||
.unlocked_ioctl = spidev_ioctl,
|
||||
};
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
/* The main reason to have this class is to make mdev/udev create the
|
||||
* /dev/spidevB.C character device nodes exposing our userspace API.
|
||||
* It also simplifies memory management.
|
||||
*/
|
||||
|
||||
static struct class *spidev_class;
|
||||
|
||||
static const struct of_device_id spidev_dt_ids[] = {
|
||||
{ .compatible = "100ask,dac" },
|
||||
{},
|
||||
};
|
||||
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
static int spidev_probe(struct spi_device *spi)
|
||||
{
|
||||
/* 1. 记录spi_device */
|
||||
dac = spi;
|
||||
|
||||
/* 2. 注册字符设备 */
|
||||
major = register_chrdev(0, "100ask_dac", &spidev_fops);
|
||||
spidev_class = class_create(THIS_MODULE, "100ask_dac");
|
||||
device_create(spidev_class, NULL, MKDEV(major, 0), NULL, "100ask_dac");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int spidev_remove(struct spi_device *spi)
|
||||
{
|
||||
/* 反注册字符设备 */
|
||||
device_destroy(spidev_class, MKDEV(major, 0));
|
||||
class_destroy(spidev_class);
|
||||
unregister_chrdev(major, "100ask_dac");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct spi_driver spidev_spi_driver = {
|
||||
.driver = {
|
||||
.name = "100ask_spi_dac_drv",
|
||||
.of_match_table = of_match_ptr(spidev_dt_ids),
|
||||
},
|
||||
.probe = spidev_probe,
|
||||
.remove = spidev_remove,
|
||||
|
||||
/* NOTE: suspend/resume methods are not necessary here.
|
||||
* We don't do anything except pass the requests to/from
|
||||
* the underlying controller. The refrigerator handles
|
||||
* most issues; the controller driver handles the rest.
|
||||
*/
|
||||
};
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
static int __init spidev_init(void)
|
||||
{
|
||||
int status;
|
||||
|
||||
printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__);
|
||||
|
||||
status = spi_register_driver(&spidev_spi_driver);
|
||||
if (status < 0) {
|
||||
}
|
||||
return status;
|
||||
}
|
||||
module_init(spidev_init);
|
||||
|
||||
static void __exit spidev_exit(void)
|
||||
{
|
||||
printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__);
|
||||
spi_unregister_driver(&spidev_spi_driver);
|
||||
}
|
||||
module_exit(spidev_exit);
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
56
IMX6ULL/source/11_SPI/06_dac_use_mydrv_ok/dac_test.c
Normal file
56
IMX6ULL/source/11_SPI/06_dac_use_mydrv_ok/dac_test.c
Normal file
@@ -0,0 +1,56 @@
|
||||
|
||||
|
||||
/* 参考: tools\spi\spidev_fdx.c */
|
||||
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
#include <stdlib.h>
|
||||
#include <fcntl.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include <linux/types.h>
|
||||
|
||||
#define SPI_IOC_WR 123
|
||||
|
||||
/* dac_test /dev/100ask_dac <val> */
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
int fd;
|
||||
unsigned int val;
|
||||
int status;
|
||||
|
||||
unsigned char tx_buf[2];
|
||||
unsigned char rx_buf[2];
|
||||
|
||||
if (argc != 3)
|
||||
{
|
||||
printf("Usage: %s /dev/100ask_dac <val>\n", argv[0]);
|
||||
return 0;
|
||||
}
|
||||
|
||||
fd = open(argv[1], O_RDWR);
|
||||
if (fd < 0) {
|
||||
printf("can not open %s\n", argv[1]);
|
||||
return 1;
|
||||
}
|
||||
|
||||
val = strtoul(argv[2], NULL, 0);
|
||||
|
||||
status = ioctl(fd, SPI_IOC_WR, &val);
|
||||
if (status < 0) {
|
||||
printf("SPI_IOC_WR\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* 打印 */
|
||||
printf("Pre val = %d\n", val);
|
||||
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -545,13 +545,18 @@ git clone https://e.coding.net/weidongshan/linux/doc_and_source_for_drivers.git
|
||||
```
|
||||
|
||||
* 2021.04.25 发布"SPI子系统":
|
||||
|
||||
|
||||
```shell
|
||||
10_OLED模块上机实验_STM32MP157
|
||||
```
|
||||
|
||||
* 2021.04.26 发布"SPI子系统":
|
||||
|
||||
|
||||
```shell
|
||||
12_编写SPI_DAC模块驱动程序
|
||||
13_编写DAC驱动_上机实验_IMX6ULL
|
||||
13_编写DAC驱动_上机实验_STM32MP157
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -4,12 +4,25 @@
|
||||
|
||||
* DAC芯片手册:`TLC5615.pdf`
|
||||
|
||||
|
||||
|
||||
|
||||
## 1. 硬件
|
||||
|
||||
### 1.1 原理图
|
||||
## 1. 要做什么事情
|
||||
|
||||

|
||||
|
||||
|
||||
|
||||
* 查看原理图,编写设备树
|
||||
* 编写驱动程序,注册一个spidrv
|
||||
* 编写测试程序
|
||||
|
||||
|
||||
|
||||
|
||||
## 2. 硬件
|
||||
|
||||
### 2.1 原理图
|
||||
|
||||
IMX6ULL:
|
||||
|
||||
@@ -27,9 +40,9 @@ STM32MP157:
|
||||
|
||||
|
||||
|
||||
### 1.2 连接
|
||||
### 2.2 连接
|
||||
|
||||
#### 1.2.1 IMX6ULL
|
||||
#### 2.2.1 IMX6ULL
|
||||
|
||||
DAC模块接到IMX6ULL扩展板的SPI_A插座上:
|
||||
|
||||
@@ -37,11 +50,11 @@ DAC模块接到IMX6ULL扩展板的SPI_A插座上:
|
||||
|
||||
|
||||
|
||||
#### 1.2.2 STM32MP157
|
||||
#### 2.2.2 STM32MP157
|
||||
|
||||
|
||||
|
||||
## 2. 编写设备树
|
||||
## 3. 编写设备树
|
||||
|
||||
确认SPI时钟最大频率:
|
||||
|
||||
@@ -66,7 +79,7 @@ F = 20000000 = 20MHz
|
||||
|
||||
|
||||
|
||||
### 2.1 IMX6ULL
|
||||
### 3.1 IMX6ULL
|
||||
|
||||

|
||||
|
||||
@@ -95,7 +108,7 @@ DAC模块接在这个插座上,那么要在设备树里spi1的节点下创建
|
||||
|
||||
|
||||
|
||||
### 2.2 STM32MP157
|
||||
### 3.2 STM32MP157
|
||||
|
||||

|
||||
|
||||
@@ -129,7 +142,7 @@ DAC模块接在这个插座上,那么要在设备树里spi5的节点下创建
|
||||
|
||||
|
||||
|
||||
## 3. 编写驱动程序
|
||||
## 4. 编写驱动程序
|
||||
|
||||
以前我们基于spidev编写过DAC的应用程序,可以参考它:
|
||||
|
||||
@@ -139,5 +152,5 @@ DAC模块接在这个插座上,那么要在设备树里spi5的节点下创建
|
||||
|
||||
|
||||
|
||||
## 4. 编写测试程序
|
||||
## 5. 编写测试程序
|
||||
|
||||
|
||||
Binary file not shown.
286
STM32MP157/doc_pic/11_SPI/13_编写DAC驱动_上机实验.md
Normal file
286
STM32MP157/doc_pic/11_SPI/13_编写DAC驱动_上机实验.md
Normal file
@@ -0,0 +1,286 @@
|
||||
# 编写DAC驱动_上机实验 #
|
||||
|
||||
* 源码
|
||||
|
||||

|
||||
|
||||
|
||||
|
||||
## 1. 硬件
|
||||
|
||||
### 1.1 原理图
|
||||
|
||||
IMX6ULL:
|
||||
|
||||

|
||||
|
||||
STM32MP157:
|
||||
|
||||

|
||||
|
||||
|
||||
|
||||
原理图:
|
||||
|
||||

|
||||
|
||||
|
||||
|
||||
### 1.2 连接
|
||||
|
||||
#### 1.2.1 IMX6ULL
|
||||
|
||||
DAC模块接到IMX6ULL扩展板的SPI_A插座上:
|
||||
|
||||

|
||||
|
||||
|
||||
|
||||
#### 1.2.2 STM32MP157
|
||||
|
||||
DAC模块接到STM32MP157扩展板的SPI_A插座上:
|
||||
|
||||

|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## 2. 编写设备树
|
||||
|
||||
确认SPI时钟最大频率:
|
||||
|
||||

|
||||
|
||||
```shell
|
||||
T = 25 + 25 = 50ns
|
||||
F = 20000000 = 20MHz
|
||||
```
|
||||
|
||||
|
||||
|
||||
设备树如下:
|
||||
|
||||
```shell
|
||||
dac: dac {
|
||||
compatible = "100ask,dac";
|
||||
reg = <0>;
|
||||
spi-max-frequency = <20000000>;
|
||||
};
|
||||
```
|
||||
|
||||
|
||||
|
||||
### 2.1 IMX6ULL
|
||||
|
||||

|
||||
|
||||
DAC模块接在这个插座上,那么要在设备树里spi1的节点下创建子节点。
|
||||
|
||||
代码在`arch/arm/boot/dts/100ask_imx6ull-14x14.dts`中,如下:
|
||||
|
||||
```shell
|
||||
&ecspi1 {
|
||||
pinctrl-names = "default";
|
||||
pinctrl-0 = <&pinctrl_ecspi1>;
|
||||
|
||||
fsl,spi-num-chipselects = <2>;
|
||||
cs-gpios = <&gpio4 26 GPIO_ACTIVE_LOW>, <&gpio4 24 GPIO_ACTIVE_LOW>;
|
||||
status = "okay";
|
||||
|
||||
dac: dac {
|
||||
compatible = "100ask,dac";
|
||||
reg = <0>;
|
||||
spi-max-frequency = <20000000>;
|
||||
};
|
||||
};
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
### 2.2 STM32MP157
|
||||
|
||||

|
||||
|
||||
DAC模块接在这个插座上,那么要在设备树里spi5的节点下创建子节点。
|
||||
|
||||
代码在`arch/arm/boot/dts/stm32mp157c-100ask-512d-lcd-v1.dts`中,如下:
|
||||
|
||||
```shell
|
||||
&spi5 {
|
||||
pinctrl-names = "default", "sleep";
|
||||
pinctrl-0 = <&spi5_pins_a>;
|
||||
pinctrl-1 = <&spi5_sleep_pins_a>;
|
||||
status = "okay";
|
||||
cs-gpios = <&gpioh 5 GPIO_ACTIVE_LOW>, <&gpioz 4 GPIO_ACTIVE_LOW>;
|
||||
spidev: icm20608@0{
|
||||
compatible = "invensense,icm20608";
|
||||
interrupts = <0 IRQ_TYPE_EDGE_FALLING>;
|
||||
interrupt-parent = <&gpioz>;
|
||||
spi-max-frequency = <8000000>;
|
||||
reg = <0>;
|
||||
};
|
||||
dac_test: dac_test@1{
|
||||
compatible = "100ask,dac";
|
||||
spi-max-frequency = <20000000>;
|
||||
reg = <1>;
|
||||
};
|
||||
};
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## 3. 编译替换设备树
|
||||
|
||||
### 3.1 IMX6ULL
|
||||
|
||||
#### 3.1.1 设置工具链
|
||||
|
||||
```shell
|
||||
export ARCH=arm
|
||||
export CROSS_COMPILE=arm-buildroot-linux-gnueabihf-
|
||||
export PATH=$PATH:/home/book/100ask_imx6ull-sdk/ToolChain/arm-buildroot-linux-gnueabihf_sdk-buildroot/bin
|
||||
```
|
||||
|
||||
|
||||
#### 3.1.2 编译、替换设备树
|
||||
|
||||
* 编译设备树:
|
||||
在Ubuntu的IMX6ULL内核目录下执行如下命令,
|
||||
得到设备树文件:`arch/arm/boot/dts/100ask_imx6ull-14x14.dtb`
|
||||
|
||||
```shell
|
||||
make dtbs
|
||||
```
|
||||
|
||||
* 复制到NFS目录:
|
||||
|
||||
```shell
|
||||
$ cp arch/arm/boot/dts/100ask_imx6ull-14x14.dtb ~/nfs_rootfs/
|
||||
```
|
||||
|
||||
* 开发板上挂载NFS文件系统
|
||||
|
||||
```shell
|
||||
[root@100ask:~]# mount -t nfs -o nolock,vers=3 192.168.1.137:/home/book/nfs_rootfs /mnt
|
||||
```
|
||||
|
||||
* 更新设备树
|
||||
|
||||
```shell
|
||||
[root@100ask:~]# cp /mnt/100ask_imx6ull-14x14.dtb /boot
|
||||
[root@100ask:~]# sync
|
||||
```
|
||||
|
||||
* 重启开发板
|
||||
|
||||
|
||||
|
||||
### 3.2 STM32MP157
|
||||
|
||||
#### 3.2.1 设置工具链
|
||||
|
||||
```shell
|
||||
export ARCH=arm
|
||||
export CROSS_COMPILE=arm-buildroot-linux-gnueabihf-
|
||||
export PATH=$PATH:/home/book/100ask_stm32mp157_pro-sdk/ToolChain/arm-buildroot-linux-gnueabihf_sdk-buildroot/bin
|
||||
```
|
||||
|
||||
|
||||
#### 3.2.2 编译、替换设备树
|
||||
|
||||
* 编译设备树:
|
||||
在Ubuntu的STM32MP157内核目录下执行如下命令,
|
||||
得到设备树文件:`arch/arm/boot/dts/stm32mp157c-100ask-512d-lcd-v1.dtb`
|
||||
|
||||
```shell
|
||||
make dtbs
|
||||
```
|
||||
|
||||
* 复制到NFS目录:
|
||||
|
||||
```shell
|
||||
$ cp arch/arm/boot/dts/stm32mp157c-100ask-512d-lcd-v1.dtb ~/nfs_rootfs/
|
||||
```
|
||||
|
||||
* 开发板上挂载NFS文件系统
|
||||
|
||||
```shell
|
||||
[root@100ask:~]# mount -t nfs -o nolock,vers=3 192.168.1.137:/home/book/nfs_rootfs /mnt
|
||||
```
|
||||
|
||||
* 确定设备树分区挂载在哪里
|
||||
|
||||
由于版本变化,STM32MP157单板上烧录的系统可能有细微差别。
|
||||
在开发板上执行`cat /proc/mounts`后,可以得到两种结果(见下图):
|
||||
|
||||
* mmcblk2p2分区挂载在/boot目录下(下图左边):无需特殊操作,下面把文件复制到/boot目录即可
|
||||
|
||||
* mmcblk2p2挂载在/mnt目录下(下图右边)
|
||||
|
||||
* 在视频里、后面文档里,都是更新/boot目录下的文件,所以要先执行以下命令重新挂载:
|
||||
* `mount /dev/mmcblk2p2 /boot`
|
||||
|
||||

|
||||
|
||||
* 更新设备树
|
||||
|
||||
```shell
|
||||
[root@100ask:~]# cp /mnt/stm32mp157c-100ask-512d-lcd-v1.dtb /boot/
|
||||
[root@100ask:~]# sync
|
||||
```
|
||||
|
||||
* 重启开发板
|
||||
|
||||
|
||||
|
||||
## 4. 编译DAC驱动
|
||||
|
||||
|
||||
|
||||
## 5. 编译APP
|
||||
|
||||
```shell
|
||||
arm-buildroot-linux-gnueabihf-gcc -o dac_test dac_test.c
|
||||
```
|
||||
|
||||
|
||||
|
||||
## 6. 上机实验
|
||||
|
||||
如果spidev没有被编译进内核,那么先执行:
|
||||
|
||||
```shell
|
||||
insmod dac_drv.ko
|
||||
```
|
||||
|
||||
|
||||
|
||||
确定设备节点:
|
||||
|
||||
```shell
|
||||
ls /dev/100ask_dac
|
||||
```
|
||||
|
||||
|
||||
|
||||
假设设备节点为`/dev/100ask_dac`,执行测试程序:
|
||||
|
||||
```shell
|
||||
./dac_test /dev/100ask_dac 500
|
||||
./dac_test /dev/100ask_dac 600
|
||||
./dac_test /dev/100ask_dac 1000
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
BIN
STM32MP157/doc_pic/11_SPI/13_编写DAC驱动_上机实验.tif
Normal file
BIN
STM32MP157/doc_pic/11_SPI/13_编写DAC驱动_上机实验.tif
Normal file
Binary file not shown.
BIN
STM32MP157/doc_pic/11_SPI/14_编写SPI_OLED模块驱动程序.tif
Normal file
BIN
STM32MP157/doc_pic/11_SPI/14_编写SPI_OLED模块驱动程序.tif
Normal file
Binary file not shown.
BIN
STM32MP157/doc_pic/11_SPI/pic/73_src_dac_drv.png
Normal file
BIN
STM32MP157/doc_pic/11_SPI/pic/73_src_dac_drv.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 16 KiB |
22
STM32MP157/source/A7/11_SPI/05_dac_use_mydrv/Makefile
Normal file
22
STM32MP157/source/A7/11_SPI/05_dac_use_mydrv/Makefile
Normal file
@@ -0,0 +1,22 @@
|
||||
|
||||
# 1. 使用不同的开发板内核时, 一定要修改KERN_DIR
|
||||
# 2. KERN_DIR中的内核要事先配置、编译, 为了能编译内核, 要先设置下列环境变量:
|
||||
# 2.1 ARCH, 比如: export ARCH=arm64
|
||||
# 2.2 CROSS_COMPILE, 比如: export CROSS_COMPILE=aarch64-linux-gnu-
|
||||
# 2.3 PATH, 比如: export PATH=$PATH:/home/book/100ask_roc-rk3399-pc/ToolChain-6.3.1/gcc-linaro-6.3.1-2017.05-x86_64_aarch64-linux-gnu/bin
|
||||
# 注意: 不同的开发板不同的编译器上述3个环境变量不一定相同,
|
||||
# 请参考各开发板的高级用户使用手册
|
||||
|
||||
KERN_DIR = /home/book/100ask_stm32mp157_pro-sdk/Linux-5.4
|
||||
|
||||
all:
|
||||
make -C $(KERN_DIR) M=`pwd` modules
|
||||
$(CROSS_COMPILE)gcc -o dac_test dac_test.c
|
||||
|
||||
clean:
|
||||
make -C $(KERN_DIR) M=`pwd` modules clean
|
||||
rm -rf modules.order dac_test
|
||||
|
||||
obj-m += dac_drv.o
|
||||
|
||||
|
||||
174
STM32MP157/source/A7/11_SPI/05_dac_use_mydrv/dac_drv.c
Normal file
174
STM32MP157/source/A7/11_SPI/05_dac_use_mydrv/dac_drv.c
Normal file
@@ -0,0 +1,174 @@
|
||||
/*
|
||||
* Simple synchronous userspace interface to SPI devices
|
||||
*
|
||||
* Copyright (C) 2006 SWAPP
|
||||
* Andrea Paterniani <a.paterniani@swapp-eng.it>
|
||||
* Copyright (C) 2007 David Brownell (simplification, cleanup)
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#include <linux/init.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/ioctl.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/list.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/compat.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/acpi.h>
|
||||
|
||||
#include <linux/spi/spi.h>
|
||||
#include <linux/spi/spidev.h>
|
||||
|
||||
#include <linux/uaccess.h>
|
||||
|
||||
#define SPI_IOC_WR 123
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
static struct spi_device *dac;
|
||||
static int major;
|
||||
|
||||
static long
|
||||
spidev_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
|
||||
{
|
||||
int val;
|
||||
int err;
|
||||
int old_val = 0;
|
||||
|
||||
struct spi_message msg;
|
||||
struct spi_transfer xfer;
|
||||
int status;
|
||||
|
||||
/* copy_from_user */
|
||||
err = copy_from_user(&val, (const void __user *)arg, sizeof(int));
|
||||
|
||||
/* 发起SPI传输: */
|
||||
|
||||
/* 1. 把val修改为正确的格式 */
|
||||
val <<= 2; /* bit0,bit1 = 0b00 */
|
||||
val &= 0xFFC; /* 只保留10bit */
|
||||
|
||||
/* 2. 发起SPI传输同时写\读 */
|
||||
/* 2.1 构造transfer
|
||||
* 2.2 加入message
|
||||
* 2.3 调用spi_sync
|
||||
*/
|
||||
xfer[0].tx_buf = &val;
|
||||
xfer[0].rx_buf = &old_val;
|
||||
xfer[0].len = 2;
|
||||
|
||||
spi_message_init(&msg);
|
||||
spi_message_add_tail(&[0], &msg);
|
||||
|
||||
status = spi_sync(dac, &msg);
|
||||
|
||||
/* 3. 修改读到的数据的格式 */
|
||||
old_val >>= 2;
|
||||
|
||||
/* copy_to_user */
|
||||
err = copy_to_user((void __user *)arg, &old_val, sizeof(int));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static const struct file_operations spidev_fops = {
|
||||
.owner = THIS_MODULE,
|
||||
/* REVISIT switch to aio primitives, so that userspace
|
||||
* gets more complete API coverage. It'll simplify things
|
||||
* too, except for the locking.
|
||||
*/
|
||||
.unlocked_ioctl = spidev_ioctl,
|
||||
};
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
/* The main reason to have this class is to make mdev/udev create the
|
||||
* /dev/spidevB.C character device nodes exposing our userspace API.
|
||||
* It also simplifies memory management.
|
||||
*/
|
||||
|
||||
static struct class *spidev_class;
|
||||
|
||||
static const struct of_device_id spidev_dt_ids[] = {
|
||||
{ .compatible = "100ask,dac" },
|
||||
{},
|
||||
};
|
||||
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
static int spidev_probe(struct spi_device *spi)
|
||||
{
|
||||
/* 1. 记录spi_device */
|
||||
dac = spi;
|
||||
|
||||
/* 2. 注册字符设备 */
|
||||
major = register_chrdev(0, "100ask_dac", &spidev_fops);
|
||||
spidev_class = class_create(THIS_MODULE, "100ask_dac");
|
||||
device_create(spidev_class, NULL, MKDEV(major, 0), NULL, "100ask_dac");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int spidev_remove(struct spi_device *spi)
|
||||
{
|
||||
/* 反注册字符设备 */
|
||||
device_destroy(spidev_class, MKDEV(major, 0));
|
||||
class_destroy(spidev_class);
|
||||
unregister_chrdev(major, "100ask_dac");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct spi_driver spidev_spi_driver = {
|
||||
.driver = {
|
||||
.name = "100ask_spi_dac_drv",
|
||||
.of_match_table = of_match_ptr(spidev_dt_ids),
|
||||
},
|
||||
.probe = spidev_probe,
|
||||
.remove = spidev_remove,
|
||||
|
||||
/* NOTE: suspend/resume methods are not necessary here.
|
||||
* We don't do anything except pass the requests to/from
|
||||
* the underlying controller. The refrigerator handles
|
||||
* most issues; the controller driver handles the rest.
|
||||
*/
|
||||
};
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
static int __init spidev_init(void)
|
||||
{
|
||||
int status;
|
||||
|
||||
status = spi_register_driver(&spidev_spi_driver);
|
||||
if (status < 0) {
|
||||
}
|
||||
return status;
|
||||
}
|
||||
module_init(spidev_init);
|
||||
|
||||
static void __exit spidev_exit(void)
|
||||
{
|
||||
spi_unregister_driver(&spidev_spi_driver);
|
||||
}
|
||||
module_exit(spidev_exit);
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
56
STM32MP157/source/A7/11_SPI/05_dac_use_mydrv/dac_test.c
Normal file
56
STM32MP157/source/A7/11_SPI/05_dac_use_mydrv/dac_test.c
Normal file
@@ -0,0 +1,56 @@
|
||||
|
||||
|
||||
/* 参考: tools\spi\spidev_fdx.c */
|
||||
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
#include <stdlib.h>
|
||||
#include <fcntl.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include <linux/types.h>
|
||||
|
||||
#define SPI_IOC_WR 123
|
||||
|
||||
/* dac_test /dev/100ask_dac <val> */
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
int fd;
|
||||
unsigned int val;
|
||||
int status;
|
||||
|
||||
unsigned char tx_buf[2];
|
||||
unsigned char rx_buf[2];
|
||||
|
||||
if (argc != 3)
|
||||
{
|
||||
printf("Usage: %s /dev/100ask_dac <val>\n", argv[0]);
|
||||
return 0;
|
||||
}
|
||||
|
||||
fd = open(argv[1], O_RDWR);
|
||||
if (fd < 0) {
|
||||
printf("can not open %s\n", argv[1]);
|
||||
return 1;
|
||||
}
|
||||
|
||||
val = strtoul(argv[2], NULL, 0);
|
||||
|
||||
status = ioctl(fd, SPI_IOC_WR, &val);
|
||||
if (status < 0) {
|
||||
printf("SPI_IOC_WR\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* 打印 */
|
||||
printf("Pre val = %d\n", val);
|
||||
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
22
STM32MP157/source/A7/11_SPI/06_dac_use_mydrv_ok/Makefile
Normal file
22
STM32MP157/source/A7/11_SPI/06_dac_use_mydrv_ok/Makefile
Normal file
@@ -0,0 +1,22 @@
|
||||
|
||||
# 1. 使用不同的开发板内核时, 一定要修改KERN_DIR
|
||||
# 2. KERN_DIR中的内核要事先配置、编译, 为了能编译内核, 要先设置下列环境变量:
|
||||
# 2.1 ARCH, 比如: export ARCH=arm64
|
||||
# 2.2 CROSS_COMPILE, 比如: export CROSS_COMPILE=aarch64-linux-gnu-
|
||||
# 2.3 PATH, 比如: export PATH=$PATH:/home/book/100ask_roc-rk3399-pc/ToolChain-6.3.1/gcc-linaro-6.3.1-2017.05-x86_64_aarch64-linux-gnu/bin
|
||||
# 注意: 不同的开发板不同的编译器上述3个环境变量不一定相同,
|
||||
# 请参考各开发板的高级用户使用手册
|
||||
|
||||
KERN_DIR = /home/book/100ask_stm32mp157_pro-sdk/Linux-5.4
|
||||
|
||||
all:
|
||||
make -C $(KERN_DIR) M=`pwd` modules
|
||||
$(CROSS_COMPILE)gcc -o dac_test dac_test.c
|
||||
|
||||
clean:
|
||||
make -C $(KERN_DIR) M=`pwd` modules clean
|
||||
rm -rf modules.order dac_test
|
||||
|
||||
obj-m += dac_drv.o
|
||||
|
||||
|
||||
186
STM32MP157/source/A7/11_SPI/06_dac_use_mydrv_ok/dac_drv.c
Normal file
186
STM32MP157/source/A7/11_SPI/06_dac_use_mydrv_ok/dac_drv.c
Normal file
@@ -0,0 +1,186 @@
|
||||
/*
|
||||
* Simple synchronous userspace interface to SPI devices
|
||||
*
|
||||
* Copyright (C) 2006 SWAPP
|
||||
* Andrea Paterniani <a.paterniani@swapp-eng.it>
|
||||
* Copyright (C) 2007 David Brownell (simplification, cleanup)
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#include <linux/init.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/ioctl.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/list.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/compat.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/acpi.h>
|
||||
|
||||
#include <linux/spi/spi.h>
|
||||
#include <linux/spi/spidev.h>
|
||||
|
||||
#include <linux/uaccess.h>
|
||||
|
||||
#define SPI_IOC_WR 123
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
static struct spi_device *dac;
|
||||
static int major;
|
||||
|
||||
static long
|
||||
spidev_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
|
||||
{
|
||||
int val;
|
||||
int err;
|
||||
unsigned char tx_buf[2];
|
||||
unsigned char rx_buf[2];
|
||||
|
||||
struct spi_message msg;
|
||||
struct spi_transfer xfer[1];
|
||||
int status;
|
||||
|
||||
memset(&xfer[0], 0, sizeof(xfer));
|
||||
|
||||
/* copy_from_user */
|
||||
err = copy_from_user(&val, (const void __user *)arg, sizeof(int));
|
||||
|
||||
printk("spidev_ioctl get val from user: %d\n", val);
|
||||
|
||||
/* 发起SPI传输: */
|
||||
|
||||
/* 1. 把val修改为正确的格式 */
|
||||
val <<= 2; /* bit0,bit1 = 0b00 */
|
||||
val &= 0xFFC; /* 只保留10bit */
|
||||
|
||||
tx_buf[1] = val & 0xff;
|
||||
tx_buf[0] = (val>>8) & 0xff;
|
||||
|
||||
/* 2. 发起SPI传输同时写\读 */
|
||||
/* 2.1 构造transfer
|
||||
* 2.2 加入message
|
||||
* 2.3 调用spi_sync
|
||||
*/
|
||||
xfer[0].tx_buf = tx_buf;
|
||||
xfer[0].rx_buf = rx_buf;
|
||||
xfer[0].len = 2;
|
||||
|
||||
spi_message_init(&msg);
|
||||
spi_message_add_tail(&xfer[0], &msg);
|
||||
|
||||
status = spi_sync(dac, &msg);
|
||||
|
||||
/* 3. 修改读到的数据的格式 */
|
||||
val = (rx_buf[0] << 8) | (rx_buf[1]);
|
||||
val >>= 2;
|
||||
|
||||
/* copy_to_user */
|
||||
err = copy_to_user((void __user *)arg, &val, sizeof(int));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static const struct file_operations spidev_fops = {
|
||||
.owner = THIS_MODULE,
|
||||
/* REVISIT switch to aio primitives, so that userspace
|
||||
* gets more complete API coverage. It'll simplify things
|
||||
* too, except for the locking.
|
||||
*/
|
||||
.unlocked_ioctl = spidev_ioctl,
|
||||
};
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
/* The main reason to have this class is to make mdev/udev create the
|
||||
* /dev/spidevB.C character device nodes exposing our userspace API.
|
||||
* It also simplifies memory management.
|
||||
*/
|
||||
|
||||
static struct class *spidev_class;
|
||||
|
||||
static const struct of_device_id spidev_dt_ids[] = {
|
||||
{ .compatible = "100ask,dac" },
|
||||
{},
|
||||
};
|
||||
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
static int spidev_probe(struct spi_device *spi)
|
||||
{
|
||||
/* 1. 记录spi_device */
|
||||
dac = spi;
|
||||
|
||||
/* 2. 注册字符设备 */
|
||||
major = register_chrdev(0, "100ask_dac", &spidev_fops);
|
||||
spidev_class = class_create(THIS_MODULE, "100ask_dac");
|
||||
device_create(spidev_class, NULL, MKDEV(major, 0), NULL, "100ask_dac");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int spidev_remove(struct spi_device *spi)
|
||||
{
|
||||
/* 反注册字符设备 */
|
||||
device_destroy(spidev_class, MKDEV(major, 0));
|
||||
class_destroy(spidev_class);
|
||||
unregister_chrdev(major, "100ask_dac");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct spi_driver spidev_spi_driver = {
|
||||
.driver = {
|
||||
.name = "100ask_spi_dac_drv",
|
||||
.of_match_table = of_match_ptr(spidev_dt_ids),
|
||||
},
|
||||
.probe = spidev_probe,
|
||||
.remove = spidev_remove,
|
||||
|
||||
/* NOTE: suspend/resume methods are not necessary here.
|
||||
* We don't do anything except pass the requests to/from
|
||||
* the underlying controller. The refrigerator handles
|
||||
* most issues; the controller driver handles the rest.
|
||||
*/
|
||||
};
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
static int __init spidev_init(void)
|
||||
{
|
||||
int status;
|
||||
|
||||
printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__);
|
||||
|
||||
status = spi_register_driver(&spidev_spi_driver);
|
||||
if (status < 0) {
|
||||
}
|
||||
return status;
|
||||
}
|
||||
module_init(spidev_init);
|
||||
|
||||
static void __exit spidev_exit(void)
|
||||
{
|
||||
printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__);
|
||||
spi_unregister_driver(&spidev_spi_driver);
|
||||
}
|
||||
module_exit(spidev_exit);
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
56
STM32MP157/source/A7/11_SPI/06_dac_use_mydrv_ok/dac_test.c
Normal file
56
STM32MP157/source/A7/11_SPI/06_dac_use_mydrv_ok/dac_test.c
Normal file
@@ -0,0 +1,56 @@
|
||||
|
||||
|
||||
/* 参考: tools\spi\spidev_fdx.c */
|
||||
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
#include <stdlib.h>
|
||||
#include <fcntl.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include <linux/types.h>
|
||||
|
||||
#define SPI_IOC_WR 123
|
||||
|
||||
/* dac_test /dev/100ask_dac <val> */
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
int fd;
|
||||
unsigned int val;
|
||||
int status;
|
||||
|
||||
unsigned char tx_buf[2];
|
||||
unsigned char rx_buf[2];
|
||||
|
||||
if (argc != 3)
|
||||
{
|
||||
printf("Usage: %s /dev/100ask_dac <val>\n", argv[0]);
|
||||
return 0;
|
||||
}
|
||||
|
||||
fd = open(argv[1], O_RDWR);
|
||||
if (fd < 0) {
|
||||
printf("can not open %s\n", argv[1]);
|
||||
return 1;
|
||||
}
|
||||
|
||||
val = strtoul(argv[2], NULL, 0);
|
||||
|
||||
status = ioctl(fd, SPI_IOC_WR, &val);
|
||||
if (status < 0) {
|
||||
printf("SPI_IOC_WR\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* 打印 */
|
||||
printf("Pre val = %d\n", val);
|
||||
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user