1.实验原理
打开扩展板原理图对照扩展板可以看到扩展板上四位数码管原理图如下:
由图中可以确定数码管为共阴极数码管,
DS_A
、
DS_B
、
DS_C
、
DS_D
、
DS_E
、
DS_F
、 DS_G、
DS_DP
分别对应数码管的每个段,对应管脚为高电平时对应的部位会被电量,因为只有一组数据信号,但是有四个数码管,所以需要通过控制 DS_G1
、
DS_G2
、
DS_G3
和DS_G4 选择某个数码管显示内容,然后通过轮询刷新让四个数码管正常显示。
M74HC595
是一个传入并出的移位寄存器,如果有多个芯片级联也可串行输出控制下一 级芯片,QA~QH
为并行输出,
QH’
为串行输出;如果本例四个
LED
数码管由两个M74HC595 控制,第一级控制数码管显示什么,第二级控制哪个数码管显示。
数码管显示码
数码管控制码所以当需要第 2个数码管显示 8时,只需要通过 SER连续输入 02 7F两个字节数据即可。
原理图中
RCLK
、
SRCLK
、
SER
分别和
STM32MP157
的
SPI4_NSS
、
SPI4_CLK、
SPI4_MOSI 连接,
74HC595
芯片手册中对于
RCLK
、
SRCLK
及
SER
的描述为:
SPI 中 NSS、CLK、MOSI 描述为: 根据上述描述在编写驱动是可以通过 GPIO 模拟 74HC595 的时钟完成控制,因为 74H595 时序和 SPI 时序兼容,所以也可通过 SPI 控制器控制 74HC595。
查看芯片手册,管脚对应关系如下:
2.实验步骤
1) 导入交叉编译工具链
linux@ubuntu:$ source /opt/st/stm32mp1/3.1-openstlinux-5.4-dunfell-mp1-20-06-24/environment-setup-cortexa7t2hf-neon-vfpv4-ostl-linux-gnueabi
2) 内核配置
74HC595
非标准
SPI
设备,且内核并没有其驱动,本节移植
spidev
通用驱动,然后通过应用程序驱动 74HC595
。
内核中有
STM32MP1
的
SPI
控制器标准驱动及
spidevi
驱动,只要在内核中配置对应选项即可,驱动路径为:
drivers/spi/spi-stm32.cdrivers/spi/spidev.c
执行
make menuconfig
配置内核对应选项
linux@ubuntu:$ make menuconfigDevice Drivers --->[*] SPI support ---><*> STMicroelectronics STM32 SPI controller<*> User mode SPI device driver support
3) 修改设备树
参考
linux
内核文档:
Documentation/devicetree/bindings/spi/spi-stm32.txt
修改设备树文件:
arch/arm/boot/dts/stm32mp157a-fsmp1a-extended.dts
由于
spi4
相关内容在
stm32mp151.dtsi
中已完成定义,这里需要在原有基础添加与硬件对应的相关信息,在文件 stm32mp157a-fsmp1a-extended.dts 末尾集成并添加 spi4 相关内容:
&spi4 { pinctrl-names = "default", "sleep"; pinctrl-0 = <&spi4_pins_a>; pinctrl-1 = <&spi4_sleep_pins_a>; cs-gpios = <&gpioe 11 0>; status = "okay"; m74hc595: m74hc595@0 { compatible = "ge,achc"; reg = <0>; spi-max-frequency = <100000>; }; };
4) 重新编译内核和设备树文件
linux@ubuntu:$ make -j4 uImage dtbs LOADADDR=0xC2000040
5) 更新系统内核和设备树
6) 测试
系统启动后可以查看目录
/dev/
root@fsmp1a:~# ls /dev/spidev*/dev/spidev0.0
如果系统中有多个
spidev
设备,这里可能会有很多个
spidev
设备,确定哪个文件是我们 的设备对应文件,可以通过查看/sys/bus/spi/devices/spi*/of_node/name
信息确认:
root@fsmp1a:~# cat /sys/bus/spi/devices/spi*/of_node/namem74hc595
*
号代表当前目录下所有设备,由显示信息可以确认
spidev0.0
是当前设备。
7) 编写测试程序测试
spi_test.c
#include <stdint.h> #include <unistd.h> #include <stdio.h> #include <stdlib.h> #include <getopt.h> #include <fcntl.h> #include <sys/ioctl.h> #include <linux/types.h> #include <linux/spi/spidev.h> #define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0])) static void pabort(const char *s) { perror(s); abort(); } static const char *device = "/dev/spidev0.0"; static uint8_t mode; static uint8_t bits = 8; static uint32_t speed = 400000; static uint16_t delay = 0; int main(int argc, char *argv[]) { int ret = 0; int fd; if (argc != 2) { printf("usage: %s <device-file>\n", argv[0]); return 1; } fd = open(device, O_RDWR); //打开设备文件 if (fd < 0) pabort("can't open device"); /* * spi mode //设置 spi 设备模式 */ ret = ioctl(fd, SPI_IOC_WR_MODE, &mode); //写模式 if (ret == -1) pabort("can't set spi mode"); /* * bits per word //设置每个字含多少位 */ ret = ioctl(fd, SPI_IOC_WR_BITS_PER_WORD, &bits); //写每个字含多少位 if (ret == -1) pabort("can't set bits per word"); /* * max speed hz //设置速率 */ ret = ioctl(fd, SPI_IOC_WR_MAX_SPEED_HZ, &speed); //写速率 if (ret == -1) pabort("can't set max speed hz"); //打印模式,每字多少位和速率信息 printf("spi mode: %d\n", mode); printf("bits per word: %d\n", bits); printf("max speed: %d Hz (%d KHz)\n", speed, speed/1000); //transfer(fd); //传输测试 int i = 0; unsigned char num[20] = {0x3f, 0x06, 0x5b, 0x4f, 0x66, 0x6d, 0x7d, 0x07, 0x7f, 0x6f, 0x77, 0x7c, 0x39, 0x5e, 0x79, 0x71}; int pos = 0; unsigned char buf[2]; while(1) { pos = 1 << (i % 4); buf[0] = pos; buf[1] = num[i]; if (write(fd, buf, 2) < 0) perror("write"); i++; if (i == 4) { i = 0; } usleep(3000); } return ret; }
交叉编译测试程序并将编译好的测试程序下载到板子上,执行程序如下,观察数码管状态:
root@fsmp1a:~# ./spi_test /dev/spidev0.0spi mode: 0bits per word: 8max speed: 400000 Hz (400 KHz)