diff --git a/IMX6ULL/doc_pic/11_SPI/17_OLED_Framebuffer驱动_上机实验.md b/IMX6ULL/doc_pic/11_SPI/17_OLED_Framebuffer驱动_上机实验.md new file mode 100644 index 0000000..2134516 --- /dev/null +++ b/IMX6ULL/doc_pic/11_SPI/17_OLED_Framebuffer驱动_上机实验.md @@ -0,0 +1,242 @@ +# OLED_Framebuffer驱动_上机实验 # + +* 源码: + + ![image-20220509140847199](pic/79_src_for_framebuffer_ok.png) + +* DMA参考文章:https://www.kernel.org/doc/html/latest/core-api/dma-api-howto.html + +## 1. 硬件 + +### 1.1 原理图 + +IMX6ULL: + +![image-20220316120535482](pic/47_imx6ull_oled_sch.png) + +STM32MP157: + +![image-20220316120427564](pic/48_stm32mp157_oled_sch.png) + + + +原理图: + +![image-20220314114847437](pic/49_oled_sch.png) + + + +### 1.2 连接 + +无论是使用IMX6ULL开发板还是STM32MP157开发板,都有类似的扩展板。把OLED模块接到扩展板的SPI_A插座上,如下: + +![image-20220314115446373](pic/50_oled_on_extend_brd.png) + + + + + +## 2. 编写设备树 + +### 2.1.1 IMX6ULL + +![](pic/44_imx6ull_pro_extend_spi_a.png) + +DC引脚使用GPIO4_20,也需在设备树里指定。 + +设备树如下: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"; + + oled: oled { + compatible = "100ask,oled"; + reg = <0>; + spi-max-frequency = <10000000>; + dc-gpios = <&gpio4 20 GPIO_ACTIVE_HIGH>; + }; +}; +``` + + + + + +### 2.1.2 STM32MP157 + +![](pic/45_stm32mp157_pro_extend_spi_a.png) + +DC引脚使用GPIOA_13,也需要在设备树里指定。 + +设备树如下:`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>; + }; + oled: oled@1{ + compatible = "100ask,oled"; + spi-max-frequency = <10000000>; + reg = <1>; + dc-gpios = <&gpioa 13 GPIO_ACTIVE_HIGH>; + }; +}; +``` + + + +## 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` + + ![](pic/46_boot_mount.png) + +* 更新设备树 + + ```shell + [root@100ask:~]# cp /mnt/stm32mp157c-100ask-512d-lcd-v1.dtb /boot/ + [root@100ask:~]# sync + ``` + +* 重启开发板 + + + +## 4. 编译OLED驱动 + +```shell +cd 10_oled_framebuffer_ok +make +``` + + + +## 5. 编译APP + +```shell +cd 10_oled_framebuffer_ok/03_freetype_show_font_angle +make +``` + + + + + +## 6. 上机实验 + +```shell +1. 安装驱动程序, 确定新出现哪个设备节点 +ls /dev/fb* +insmod oled_drv.ko +ls /dev/fb* + +2. 在IMX6ULL上运行测试程序 +./freetype_show_font_angle /dev/fb2 ./simsun.ttc 0 20 + +2. 在STM32MP157上运行测试程序 +./freetype_show_font_angle /dev/fb1 ./simsun.ttc 0 20 +``` + diff --git a/IMX6ULL/doc_pic/11_SPI/17_OLED_Framebuffer驱动_上机实验.tif b/IMX6ULL/doc_pic/11_SPI/17_OLED_Framebuffer驱动_上机实验.tif new file mode 100644 index 0000000..0fc2d72 Binary files /dev/null and b/IMX6ULL/doc_pic/11_SPI/17_OLED_Framebuffer驱动_上机实验.tif differ diff --git a/IMX6ULL/doc_pic/11_SPI/pic/79_src_for_framebuffer_ok.png b/IMX6ULL/doc_pic/11_SPI/pic/79_src_for_framebuffer_ok.png new file mode 100644 index 0000000..eeded1a Binary files /dev/null and b/IMX6ULL/doc_pic/11_SPI/pic/79_src_for_framebuffer_ok.png differ diff --git a/IMX6ULL/source/11_SPI/10_oled_framebuffer_ok/03_freetype_show_font_angle/Makefile b/IMX6ULL/source/11_SPI/10_oled_framebuffer_ok/03_freetype_show_font_angle/Makefile new file mode 100644 index 0000000..9a2924b --- /dev/null +++ b/IMX6ULL/source/11_SPI/10_oled_framebuffer_ok/03_freetype_show_font_angle/Makefile @@ -0,0 +1,18 @@ + +# 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个环境变量不一定相同, +# 请参考各开发板的高级用户使用手册 + + +all: + $(CROSS_COMPILE)gcc -I /home/book/100ask_imx6ull-sdk/ToolChain/arm-buildroot-linux-gnueabihf_sdk-buildroot/arm-buildroot-linux-gnueabihf/sysroot/usr/include/freetype2 -o freetype_show_font_angle freetype_show_font_angle.c -lm -lfreetype + +clean: + rm -rf *.o freetype_show_font_angle + + + diff --git a/IMX6ULL/source/11_SPI/10_oled_framebuffer_ok/03_freetype_show_font_angle/freetype_show_font_angle.c b/IMX6ULL/source/11_SPI/10_oled_framebuffer_ok/03_freetype_show_font_angle/freetype_show_font_angle.c new file mode 100644 index 0000000..fb2b994 --- /dev/null +++ b/IMX6ULL/source/11_SPI/10_oled_framebuffer_ok/03_freetype_show_font_angle/freetype_show_font_angle.c @@ -0,0 +1,210 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include FT_FREETYPE_H +#include FT_GLYPH_H + +int fd_fb; +struct fb_var_screeninfo var; /* Current var */ +int screen_size; +unsigned char *fbmem; +unsigned int line_width; +unsigned int pixel_width; + + +/********************************************************************** + * 函数名称: lcd_put_pixel + * 功能描述: 在LCD指定位置上输出指定颜色(描点) + * 输入参数: x坐标,y坐标,颜色 + * 输出参数: 无 + * 返 回 值: 会 + * 修改日期 版本号 修改人 修改内容 + * ----------------------------------------------- + * 2020/05/12 V1.0 zh(angenao) 创建 + ***********************************************************************/ +void lcd_put_pixel(int x, int y, unsigned int color) +{ + unsigned char *pen_8 = fbmem+y*line_width+x*pixel_width; + unsigned short *pen_16; + unsigned int *pen_32; + + unsigned int red, green, blue; + int bit; + + pen_16 = (unsigned short *)pen_8; + pen_32 = (unsigned int *)pen_8; + + switch (var.bits_per_pixel) + { + case 1: + { + pen_8 = fbmem+y*line_width+x/8; + bit = x & 7; + if (color) + *pen_8 = *pen_8 | (1<> 16) & 0xff; + green = (color >> 8) & 0xff; + blue = (color >> 0) & 0xff; + color = ((red >> 3) << 11) | ((green >> 2) << 5) | (blue >> 3); + *pen_16 = color; + break; + } + case 32: + { + *pen_32 = color; + break; + } + default: + { + printf("can't surport %dbpp\n", var.bits_per_pixel); + break; + } + } +} + +/********************************************************************** + * 函数名称: draw_bitmap + * 功能描述: 根据bitmap位图,在LCD指定位置显示汉字 + * 输入参数: x坐标,y坐标,位图指针 + * 输出参数: 无 + * 返 回 值: 无 + * 修改日期 版本号 修改人 修改内容 + * ----------------------------------------------- + * 2020/05/12 V1.0 zh(angenao) 创建 + ***********************************************************************/ +void +draw_bitmap( FT_Bitmap* bitmap, + FT_Int x, + FT_Int y) +{ + FT_Int i, j, p, q; + FT_Int x_max = x + bitmap->width; + FT_Int y_max = y + bitmap->rows; + + //printf("x = %d, y = %d\n", x, y); + + for ( j = y, q = 0; j < y_max; j++, q++ ) + { + for ( i = x, p = 0; i < x_max; i++, p++ ) + { + if ( i < 0 || j < 0 || + i >= var.xres || j >= var.yres ) + continue; + + //image[j][i] |= bitmap->buffer[q * bitmap->width + p]; + lcd_put_pixel(i, j, bitmap->buffer[q * bitmap->width + p]); + } + } +} + + +int main(int argc, char **argv) +{ + wchar_t *chinese_str = L"繁"; + + FT_Library library; + FT_Face face; + int error; + FT_Vector pen; + FT_GlyphSlot slot; + int font_size = 24; + FT_Matrix matrix; /* transformation matrix */ + double angle; + + if (argc < 3) + { + printf("Usage : %s [font_size]\n", argv[0]); + return -1; + } + + angle = ( 1.0* strtoul(argv[3], NULL, 0) / 360 ) * 3.14159 * 2; /* use 25 degrees */ + + if (argc == 5) + font_size = strtoul(argv[4], NULL, 0); + + fd_fb = open(argv[1], O_RDWR); + if (fd_fb < 0) + { + printf("can't open %s\n", argv[1]); + return -1; + } + + if (ioctl(fd_fb, FBIOGET_VSCREENINFO, &var)) + { + printf("can't get var\n"); + return -1; + } + + line_width = var.xres * var.bits_per_pixel / 8; + pixel_width = var.bits_per_pixel / 8; + screen_size = var.xres * var.yres * var.bits_per_pixel / 8; + fbmem = (unsigned char *)mmap(NULL , screen_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd_fb, 0); + if (fbmem == (unsigned char *)-1) + { + printf("can't mmap\n"); + return -1; + } + + /* 清屏: 全部设为黑色 */ + memset(fbmem, 0, screen_size); + + /* 显示矢量字体 */ + error = FT_Init_FreeType( &library ); /* initialize library */ + /* error handling omitted */ + + error = FT_New_Face( library, argv[2], 0, &face ); /* create face object */ + /* error handling omitted */ + slot = face->glyph; + + FT_Set_Pixel_Sizes(face, font_size, 0); + + /* 确定座标: + */ + pen.x = 0; + pen.y = 0; + + /* set up matrix */ + matrix.xx = (FT_Fixed)( cos( angle ) * 0x10000L ); + matrix.xy = (FT_Fixed)(-sin( angle ) * 0x10000L ); + matrix.yx = (FT_Fixed)( sin( angle ) * 0x10000L ); + matrix.yy = (FT_Fixed)( cos( angle ) * 0x10000L ); + + /* set transformation */ + FT_Set_Transform( face, &matrix, &pen); + + /* load glyph image into the slot (erase previous one) */ + error = FT_Load_Char( face, chinese_str[0], FT_LOAD_RENDER ); + if (error) + { + printf("FT_Load_Char error\n"); + return -1; + } + + draw_bitmap( &slot->bitmap, + var.xres/2, + var.yres/2); + + return 0; +} + diff --git a/IMX6ULL/source/11_SPI/10_oled_framebuffer_ok/03_freetype_show_font_angle/simsun.ttc b/IMX6ULL/source/11_SPI/10_oled_framebuffer_ok/03_freetype_show_font_angle/simsun.ttc new file mode 100644 index 0000000..23d5c4a Binary files /dev/null and b/IMX6ULL/source/11_SPI/10_oled_framebuffer_ok/03_freetype_show_font_angle/simsun.ttc differ diff --git a/IMX6ULL/source/11_SPI/10_oled_framebuffer_ok/Makefile b/IMX6ULL/source/11_SPI/10_oled_framebuffer_ok/Makefile new file mode 100644 index 0000000..2b47cb9 --- /dev/null +++ b/IMX6ULL/source/11_SPI/10_oled_framebuffer_ok/Makefile @@ -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 spi_oled spi_oled.c + +clean: + make -C $(KERN_DIR) M=`pwd` modules clean + rm -rf modules.order spi_oled + +obj-m += oled_drv.o + + diff --git a/IMX6ULL/source/11_SPI/10_oled_framebuffer_ok/font.h b/IMX6ULL/source/11_SPI/10_oled_framebuffer_ok/font.h new file mode 100644 index 0000000..5698917 --- /dev/null +++ b/IMX6ULL/source/11_SPI/10_oled_framebuffer_ok/font.h @@ -0,0 +1,106 @@ +#ifndef _FONT_H_ +#define _FONT_H_ +const unsigned char oled_asc2_8x16[95][16]= +{ + {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},// 0 + {0x00,0x00,0x00,0xF8,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x33,0x30,0x00,0x00,0x00},//!1 + {0x00,0x10,0x0C,0x06,0x10,0x0C,0x06,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},//"2 + {0x40,0xC0,0x78,0x40,0xC0,0x78,0x40,0x00,0x04,0x3F,0x04,0x04,0x3F,0x04,0x04,0x00},//#3 + {0x00,0x70,0x88,0xFC,0x08,0x30,0x00,0x00,0x00,0x18,0x20,0xFF,0x21,0x1E,0x00,0x00},//$4 + {0xF0,0x08,0xF0,0x00,0xE0,0x18,0x00,0x00,0x00,0x21,0x1C,0x03,0x1E,0x21,0x1E,0x00},//%5 + {0x00,0xF0,0x08,0x88,0x70,0x00,0x00,0x00,0x1E,0x21,0x23,0x24,0x19,0x27,0x21,0x10},//&6 + {0x10,0x16,0x0E,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},//'7 + {0x00,0x00,0x00,0xE0,0x18,0x04,0x02,0x00,0x00,0x00,0x00,0x07,0x18,0x20,0x40,0x00},//(8 + {0x00,0x02,0x04,0x18,0xE0,0x00,0x00,0x00,0x00,0x40,0x20,0x18,0x07,0x00,0x00,0x00},//)9 + {0x40,0x40,0x80,0xF0,0x80,0x40,0x40,0x00,0x02,0x02,0x01,0x0F,0x01,0x02,0x02,0x00},//*10 + {0x00,0x00,0x00,0xF0,0x00,0x00,0x00,0x00,0x01,0x01,0x01,0x1F,0x01,0x01,0x01,0x00},//+11 + {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x80,0xB0,0x70,0x00,0x00,0x00,0x00,0x00},//,12 + {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x01,0x01,0x01,0x01,0x01,0x01},//-13 + {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x30,0x30,0x00,0x00,0x00,0x00,0x00},//.14 + {0x00,0x00,0x00,0x00,0x80,0x60,0x18,0x04,0x00,0x60,0x18,0x06,0x01,0x00,0x00,0x00},///15 + {0x00,0xE0,0x10,0x08,0x08,0x10,0xE0,0x00,0x00,0x0F,0x10,0x20,0x20,0x10,0x0F,0x00},//016 + {0x00,0x10,0x10,0xF8,0x00,0x00,0x00,0x00,0x00,0x20,0x20,0x3F,0x20,0x20,0x00,0x00},//117 + {0x00,0x70,0x08,0x08,0x08,0x88,0x70,0x00,0x00,0x30,0x28,0x24,0x22,0x21,0x30,0x00},//218 + {0x00,0x30,0x08,0x88,0x88,0x48,0x30,0x00,0x00,0x18,0x20,0x20,0x20,0x11,0x0E,0x00},//319 + {0x00,0x00,0xC0,0x20,0x10,0xF8,0x00,0x00,0x00,0x07,0x04,0x24,0x24,0x3F,0x24,0x00},//420 + {0x00,0xF8,0x08,0x88,0x88,0x08,0x08,0x00,0x00,0x19,0x21,0x20,0x20,0x11,0x0E,0x00},//521 + {0x00,0xE0,0x10,0x88,0x88,0x18,0x00,0x00,0x00,0x0F,0x11,0x20,0x20,0x11,0x0E,0x00},//622 + {0x00,0x38,0x08,0x08,0xC8,0x38,0x08,0x00,0x00,0x00,0x00,0x3F,0x00,0x00,0x00,0x00},//723 + {0x00,0x70,0x88,0x08,0x08,0x88,0x70,0x00,0x00,0x1C,0x22,0x21,0x21,0x22,0x1C,0x00},//824 + {0x00,0xE0,0x10,0x08,0x08,0x10,0xE0,0x00,0x00,0x00,0x31,0x22,0x22,0x11,0x0F,0x00},//925 + {0x00,0x00,0x00,0xC0,0xC0,0x00,0x00,0x00,0x00,0x00,0x00,0x30,0x30,0x00,0x00,0x00},//:26 + {0x00,0x00,0x00,0x80,0x00,0x00,0x00,0x00,0x00,0x00,0x80,0x60,0x00,0x00,0x00,0x00},//;27 + {0x00,0x00,0x80,0x40,0x20,0x10,0x08,0x00,0x00,0x01,0x02,0x04,0x08,0x10,0x20,0x00},//<28 + {0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x00,0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x00},//=29 + {0x00,0x08,0x10,0x20,0x40,0x80,0x00,0x00,0x00,0x20,0x10,0x08,0x04,0x02,0x01,0x00},//>30 + {0x00,0x70,0x48,0x08,0x08,0x08,0xF0,0x00,0x00,0x00,0x00,0x30,0x36,0x01,0x00,0x00},//?31 + {0xC0,0x30,0xC8,0x28,0xE8,0x10,0xE0,0x00,0x07,0x18,0x27,0x24,0x23,0x14,0x0B,0x00},//@32 + {0x00,0x00,0xC0,0x38,0xE0,0x00,0x00,0x00,0x20,0x3C,0x23,0x02,0x02,0x27,0x38,0x20},//A33 + {0x08,0xF8,0x88,0x88,0x88,0x70,0x00,0x00,0x20,0x3F,0x20,0x20,0x20,0x11,0x0E,0x00},//B34 + {0xC0,0x30,0x08,0x08,0x08,0x08,0x38,0x00,0x07,0x18,0x20,0x20,0x20,0x10,0x08,0x00},//C35 + {0x08,0xF8,0x08,0x08,0x08,0x10,0xE0,0x00,0x20,0x3F,0x20,0x20,0x20,0x10,0x0F,0x00},//D36 + {0x08,0xF8,0x88,0x88,0xE8,0x08,0x10,0x00,0x20,0x3F,0x20,0x20,0x23,0x20,0x18,0x00},//E37 + {0x08,0xF8,0x88,0x88,0xE8,0x08,0x10,0x00,0x20,0x3F,0x20,0x00,0x03,0x00,0x00,0x00},//F38 + {0xC0,0x30,0x08,0x08,0x08,0x38,0x00,0x00,0x07,0x18,0x20,0x20,0x22,0x1E,0x02,0x00},//G39 + {0x08,0xF8,0x08,0x00,0x00,0x08,0xF8,0x08,0x20,0x3F,0x21,0x01,0x01,0x21,0x3F,0x20},//H40 + {0x00,0x08,0x08,0xF8,0x08,0x08,0x00,0x00,0x00,0x20,0x20,0x3F,0x20,0x20,0x00,0x00},//I41 + {0x00,0x00,0x08,0x08,0xF8,0x08,0x08,0x00,0xC0,0x80,0x80,0x80,0x7F,0x00,0x00,0x00},//J42 + {0x08,0xF8,0x88,0xC0,0x28,0x18,0x08,0x00,0x20,0x3F,0x20,0x01,0x26,0x38,0x20,0x00},//K43 + {0x08,0xF8,0x08,0x00,0x00,0x00,0x00,0x00,0x20,0x3F,0x20,0x20,0x20,0x20,0x30,0x00},//L44 + {0x08,0xF8,0xF8,0x00,0xF8,0xF8,0x08,0x00,0x20,0x3F,0x00,0x3F,0x00,0x3F,0x20,0x00},//M45 + {0x08,0xF8,0x30,0xC0,0x00,0x08,0xF8,0x08,0x20,0x3F,0x20,0x00,0x07,0x18,0x3F,0x00},//N46 + {0xE0,0x10,0x08,0x08,0x08,0x10,0xE0,0x00,0x0F,0x10,0x20,0x20,0x20,0x10,0x0F,0x00},//O47 + {0x08,0xF8,0x08,0x08,0x08,0x08,0xF0,0x00,0x20,0x3F,0x21,0x01,0x01,0x01,0x00,0x00},//P48 + {0xE0,0x10,0x08,0x08,0x08,0x10,0xE0,0x00,0x0F,0x18,0x24,0x24,0x38,0x50,0x4F,0x00},//Q49 + {0x08,0xF8,0x88,0x88,0x88,0x88,0x70,0x00,0x20,0x3F,0x20,0x00,0x03,0x0C,0x30,0x20},//R50 + {0x00,0x70,0x88,0x08,0x08,0x08,0x38,0x00,0x00,0x38,0x20,0x21,0x21,0x22,0x1C,0x00},//S51 + {0x18,0x08,0x08,0xF8,0x08,0x08,0x18,0x00,0x00,0x00,0x20,0x3F,0x20,0x00,0x00,0x00},//T52 + {0x08,0xF8,0x08,0x00,0x00,0x08,0xF8,0x08,0x00,0x1F,0x20,0x20,0x20,0x20,0x1F,0x00},//U53 + {0x08,0x78,0x88,0x00,0x00,0xC8,0x38,0x08,0x00,0x00,0x07,0x38,0x0E,0x01,0x00,0x00},//V54 + {0xF8,0x08,0x00,0xF8,0x00,0x08,0xF8,0x00,0x03,0x3C,0x07,0x00,0x07,0x3C,0x03,0x00},//W55 + {0x08,0x18,0x68,0x80,0x80,0x68,0x18,0x08,0x20,0x30,0x2C,0x03,0x03,0x2C,0x30,0x20},//X56 + {0x08,0x38,0xC8,0x00,0xC8,0x38,0x08,0x00,0x00,0x00,0x20,0x3F,0x20,0x00,0x00,0x00},//Y57 + {0x10,0x08,0x08,0x08,0xC8,0x38,0x08,0x00,0x20,0x38,0x26,0x21,0x20,0x20,0x18,0x00},//Z58 + {0x00,0x00,0x00,0xFE,0x02,0x02,0x02,0x00,0x00,0x00,0x00,0x7F,0x40,0x40,0x40,0x00},//[59 + {0x00,0x0C,0x30,0xC0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x06,0x38,0xC0,0x00},//\60 + {0x00,0x02,0x02,0x02,0xFE,0x00,0x00,0x00,0x00,0x40,0x40,0x40,0x7F,0x00,0x00,0x00},//]61 + {0x00,0x00,0x04,0x02,0x02,0x02,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},//^62 + {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80},//_63 + {0x00,0x02,0x02,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},//`64 + {0x00,0x00,0x80,0x80,0x80,0x80,0x00,0x00,0x00,0x19,0x24,0x22,0x22,0x22,0x3F,0x20},//a65 + {0x08,0xF8,0x00,0x80,0x80,0x00,0x00,0x00,0x00,0x3F,0x11,0x20,0x20,0x11,0x0E,0x00},//b66 + {0x00,0x00,0x00,0x80,0x80,0x80,0x00,0x00,0x00,0x0E,0x11,0x20,0x20,0x20,0x11,0x00},//c67 + {0x00,0x00,0x00,0x80,0x80,0x88,0xF8,0x00,0x00,0x0E,0x11,0x20,0x20,0x10,0x3F,0x20},//d68 + {0x00,0x00,0x80,0x80,0x80,0x80,0x00,0x00,0x00,0x1F,0x22,0x22,0x22,0x22,0x13,0x00},//e69 + {0x00,0x80,0x80,0xF0,0x88,0x88,0x88,0x18,0x00,0x20,0x20,0x3F,0x20,0x20,0x00,0x00},//f70 + {0x00,0x00,0x80,0x80,0x80,0x80,0x80,0x00,0x00,0x6B,0x94,0x94,0x94,0x93,0x60,0x00},//g71 + {0x08,0xF8,0x00,0x80,0x80,0x80,0x00,0x00,0x20,0x3F,0x21,0x00,0x00,0x20,0x3F,0x20},//h72 + {0x00,0x80,0x98,0x98,0x00,0x00,0x00,0x00,0x00,0x20,0x20,0x3F,0x20,0x20,0x00,0x00},//i73 + {0x00,0x00,0x00,0x80,0x98,0x98,0x00,0x00,0x00,0xC0,0x80,0x80,0x80,0x7F,0x00,0x00},//j74 + {0x08,0xF8,0x00,0x00,0x80,0x80,0x80,0x00,0x20,0x3F,0x24,0x02,0x2D,0x30,0x20,0x00},//k75 + {0x00,0x08,0x08,0xF8,0x00,0x00,0x00,0x00,0x00,0x20,0x20,0x3F,0x20,0x20,0x00,0x00},//l76 + {0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x00,0x20,0x3F,0x20,0x00,0x3F,0x20,0x00,0x3F},//m77 + {0x80,0x80,0x00,0x80,0x80,0x80,0x00,0x00,0x20,0x3F,0x21,0x00,0x00,0x20,0x3F,0x20},//n78 + {0x00,0x00,0x80,0x80,0x80,0x80,0x00,0x00,0x00,0x1F,0x20,0x20,0x20,0x20,0x1F,0x00},//o79 + {0x80,0x80,0x00,0x80,0x80,0x00,0x00,0x00,0x80,0xFF,0xA1,0x20,0x20,0x11,0x0E,0x00},//p80 + {0x00,0x00,0x00,0x80,0x80,0x80,0x80,0x00,0x00,0x0E,0x11,0x20,0x20,0xA0,0xFF,0x80},//q81 + {0x80,0x80,0x80,0x00,0x80,0x80,0x80,0x00,0x20,0x20,0x3F,0x21,0x20,0x00,0x01,0x00},//r82 + {0x00,0x00,0x80,0x80,0x80,0x80,0x80,0x00,0x00,0x33,0x24,0x24,0x24,0x24,0x19,0x00},//s83 + {0x00,0x80,0x80,0xE0,0x80,0x80,0x00,0x00,0x00,0x00,0x00,0x1F,0x20,0x20,0x00,0x00},//t84 + {0x80,0x80,0x00,0x00,0x00,0x80,0x80,0x00,0x00,0x1F,0x20,0x20,0x20,0x10,0x3F,0x20},//u85 + {0x80,0x80,0x80,0x00,0x00,0x80,0x80,0x80,0x00,0x01,0x0E,0x30,0x08,0x06,0x01,0x00},//v86 + {0x80,0x80,0x00,0x80,0x00,0x80,0x80,0x80,0x0F,0x30,0x0C,0x03,0x0C,0x30,0x0F,0x00},//w87 + {0x00,0x80,0x80,0x00,0x80,0x80,0x80,0x00,0x00,0x20,0x31,0x2E,0x0E,0x31,0x20,0x00},//x88 + {0x80,0x80,0x80,0x00,0x00,0x80,0x80,0x80,0x80,0x81,0x8E,0x70,0x18,0x06,0x01,0x00},//y89 + {0x00,0x80,0x80,0x80,0x80,0x80,0x80,0x00,0x00,0x21,0x30,0x2C,0x22,0x21,0x30,0x00},//z90 + {0x00,0x00,0x00,0x00,0x80,0x7C,0x02,0x02,0x00,0x00,0x00,0x00,0x00,0x3F,0x40,0x40},//{91 + {0x00,0x00,0x00,0x00,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0x00,0x00,0x00},//|92 + {0x00,0x02,0x02,0x7C,0x80,0x00,0x00,0x00,0x00,0x40,0x40,0x3F,0x00,0x00,0x00,0x00},//}93 + {0x00,0x06,0x01,0x01,0x02,0x02,0x04,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},//~94 +}; +const unsigned char hz_1616[][32]={ +{0x02,0x00,0x02,0x00,0xE2,0xFF,0x22,0x42,0x22,0x42,0x32,0x42,0x2A,0x42,0x26,0x42,0x22,0x42,0x22,0x42,0x22,0x42,0x22,0x42,0xE2,0xFF,0x02,0x00,0x02,0x00,0x00,0x00},/*"百",0*/ +{0x00,0x00,0xF8,0xFF,0x01,0x00,0x02,0x00,0x00,0x00,0xE2,0x1F,0x22,0x08,0x22,0x08,0x22,0x08,0xE2,0x1F,0x02,0x00,0x02,0x40,0x02,0x80,0xFE,0x7F,0x00,0x00,0x00,0x00},/*"问",1*/ +{0x00,0x00,0xFE,0xFF,0x02,0x10,0x22,0x08,0x42,0x06,0x82,0x01,0x72,0x0E,0x02,0x10,0x22,0x08,0x42,0x06,0x82,0x01,0x72,0x4E,0x02,0x80,0xFE,0x7F,0x00,0x00,0x00,0x00},/*"网",2*/ +}; +#endif diff --git a/IMX6ULL/source/11_SPI/10_oled_framebuffer_ok/oled_drv.c b/IMX6ULL/source/11_SPI/10_oled_framebuffer_ok/oled_drv.c new file mode 100644 index 0000000..5206e30 --- /dev/null +++ b/IMX6ULL/source/11_SPI/10_oled_framebuffer_ok/oled_drv.c @@ -0,0 +1,490 @@ +/* + * Simple synchronous userspace interface to SPI devices + * + * Copyright (C) 2006 SWAPP + * Andrea Paterniani + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include + +#include +#include +#include + +#define OLED_IOC_INIT 123 +#define OLED_IOC_SET_POS 124 + +//为0 表示命令,为1表示数据 +#define OLED_CMD 0 +#define OLED_DATA 1 + +static struct fb_info *myfb_info; + +static unsigned int pseudo_palette[16]; + +static struct task_struct *oled_thread; + +static unsigned char *oled_buf; //[1024]; + +/* from pxafb.c */ +static inline unsigned int chan_to_field(unsigned int chan, + struct fb_bitfield *bf) +{ + chan &= 0xffff; + chan >>= 16 - bf->length; + return chan << bf->offset; +} + +static int mylcd_setcolreg(unsigned regno, + unsigned red, unsigned green, unsigned blue, + unsigned transp, struct fb_info *info) +{ + unsigned int val; + + /* dprintk("setcol: regno=%d, rgb=%d,%d,%d\n", + regno, red, green, blue); */ + + switch (info->fix.visual) { + case FB_VISUAL_TRUECOLOR: + /* true-colour, use pseudo-palette */ + + if (regno < 16) { + u32 *pal = info->pseudo_palette; + + val = chan_to_field(red, &info->var.red); + val |= chan_to_field(green, &info->var.green); + val |= chan_to_field(blue, &info->var.blue); + + pal[regno] = val; + } + break; + + default: + return 1; /* unknown type */ + } + + return 0; +} + + +static struct fb_ops myfb_ops = { + .owner = THIS_MODULE, + .fb_setcolreg = mylcd_setcolreg, + .fb_fillrect = cfb_fillrect, + .fb_copyarea = cfb_copyarea, + .fb_imageblit = cfb_imageblit, +}; + + + +/*-------------------------------------------------------------------------*/ + +static struct spi_device *oled; +static int major; +static struct gpio_desc *dc_gpio; + +static void dc_pin_init(void) +{ + gpiod_direction_output(dc_gpio, 1); +} + +static void oled_set_dc_pin(int val) +{ + gpiod_set_value(dc_gpio, val); +} + +static void spi_write_datas(const unsigned char *buf, int len) +{ + spi_write(oled, buf, len); +} + + +/********************************************************************** + * 函数名称: oled_write_cmd + * 功能描述: oled向特定地址写入数据或者命令 + * 输入参数:@uc_data :要写入的数据 + @uc_cmd:为1则表示写入数据,为0表示写入命令 + * 输出参数:无 + * 返 回 值: 无 + * 修改日期 版本号 修改人 修改内容 + * ----------------------------------------------- + * 2020/03/04 V1.0 芯晓 创建 + ***********************************************************************/ +static void oled_write_cmd_data(unsigned char uc_data,unsigned char uc_cmd) +{ + if(uc_cmd==0) + { + oled_set_dc_pin(0); + } + else + { + oled_set_dc_pin(1);//拉高,表示写入数据 + } + spi_write_datas(&uc_data, 1);//写入 +} + + +/********************************************************************** + * 函数名称: oled_init + * 功能描述: oled_init的初始化,包括SPI控制器得初始化 + * 输入参数:无 + * 输出参数: 初始化的结果 + * 返 回 值: 成功则返回0,否则返回-1 + * 修改日期 版本号 修改人 修改内容 + * ----------------------------------------------- + * 2020/03/15 V1.0 芯晓 创建 + ***********************************************************************/ +static int oled_init(void) +{ + oled_write_cmd_data(0xae,OLED_CMD);//关闭显示 + + oled_write_cmd_data(0x00,OLED_CMD);//设置 lower column address + oled_write_cmd_data(0x10,OLED_CMD);//设置 higher column address + + oled_write_cmd_data(0x40,OLED_CMD);//设置 display start line + + oled_write_cmd_data(0xB0,OLED_CMD);//设置page address + + oled_write_cmd_data(0x81,OLED_CMD);// contract control + oled_write_cmd_data(0x66,OLED_CMD);//128 + + oled_write_cmd_data(0xa1,OLED_CMD);//设置 segment remap + + oled_write_cmd_data(0xa6,OLED_CMD);//normal /reverse + + oled_write_cmd_data(0xa8,OLED_CMD);//multiple ratio + oled_write_cmd_data(0x3f,OLED_CMD);//duty = 1/64 + + oled_write_cmd_data(0xc8,OLED_CMD);//com scan direction + + oled_write_cmd_data(0xd3,OLED_CMD);//set displat offset + oled_write_cmd_data(0x00,OLED_CMD);// + + oled_write_cmd_data(0xd5,OLED_CMD);//set osc division + oled_write_cmd_data(0x80,OLED_CMD);// + + oled_write_cmd_data(0xd9,OLED_CMD);//ser pre-charge period + oled_write_cmd_data(0x1f,OLED_CMD);// + + oled_write_cmd_data(0xda,OLED_CMD);//set com pins + oled_write_cmd_data(0x12,OLED_CMD);// + + oled_write_cmd_data(0xdb,OLED_CMD);//set vcomh + oled_write_cmd_data(0x30,OLED_CMD);// + + oled_write_cmd_data(0x8d,OLED_CMD);//set charge pump disable + oled_write_cmd_data(0x14,OLED_CMD);// + + oled_write_cmd_data(0xaf,OLED_CMD);//set dispkay on + + return 0; +} + +//坐标设置 +/********************************************************************** + * 函数名称: OLED_DIsp_Set_Pos + * 功能描述:设置要显示的位置 + * 输入参数:@ x :要显示的column address + @y :要显示的page address + * 输出参数: 无 + * 返 回 值: + * 修改日期 版本号 修改人 修改内容 + * ----------------------------------------------- + * 2020/03/15 V1.0 芯晓 创建 + ***********************************************************************/ +static void OLED_DIsp_Set_Pos(int x, int y) +{ oled_write_cmd_data(0xb0+y,OLED_CMD); + oled_write_cmd_data((x&0x0f),OLED_CMD); + oled_write_cmd_data(((x&0xf0)>>4)|0x10,OLED_CMD); +} + + +static long +spidev_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) +{ + int x, y; + + /* 根据cmd操作硬件 */ + switch (cmd) + { + case OLED_IOC_INIT: /* init */ + { + dc_pin_init(); + oled_init(); + break; + } + + case OLED_IOC_SET_POS: /* set pos */ + { + x = arg & 0xff; + y = (arg >> 8) & 0xff; + OLED_DIsp_Set_Pos(x, y); + break; + } + + } + + return 0; +} + +static ssize_t +spidev_write(struct file *filp, const char __user *buf, + size_t count, loff_t *f_pos) +{ + char *ker_buf; + int err; + + ker_buf = kmalloc(count, GFP_KERNEL); + err = copy_from_user(ker_buf, buf, count); + + oled_set_dc_pin(1);//拉高,表示写入数据 + spi_write_datas(ker_buf, count); + kfree(ker_buf); + return count; +} + +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. + */ + .write = spidev_write, + .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,oled" }, + {}, +}; + + + +static int oled_thread_func(void *param) +{ + unsigned char *p[8]; + unsigned char data[8]; + int i; + int j; + int line; + int bit; + unsigned char byte; + unsigned char *fb = myfb_info->screen_base; + int k; + + while (!kthread_should_stop()) + { + /* 1. 从Framebuffer得到数据 */ + /* 2. 转换格式 */ + k = 0; + for (i = 0; i < 8; i++) + { + for (line = 0; line < 8; line++) + p[line] = &fb[i*128 + line * 16]; + + for (j = 0; j < 16; j++) + { + for (line = 0; line < 8; line++) + { + data[line] = *p[line]; + p[line] += 1; + } + + for (bit = 0; bit < 8; bit++) + { + byte = (((data[0]>>bit) & 1) << 0) | + (((data[1]>>bit) & 1) << 1) | + (((data[2]>>bit) & 1) << 2) | + (((data[3]>>bit) & 1) << 3) | + (((data[4]>>bit) & 1) << 4) | + (((data[5]>>bit) & 1) << 5) | + (((data[6]>>bit) & 1) << 6) | + (((data[7]>>bit) & 1) << 7); + + oled_buf[k++] = byte; + } + + } + } + + + /* 3. 通过SPI发送给OLED */ + for (i = 0; i < 8; i++) + { + OLED_DIsp_Set_Pos(0, i); + oled_set_dc_pin(1); + spi_write_datas(&oled_buf[i*128], 128); + } + + + /* 4. 休眠一会 */ + schedule_timeout_interruptible(HZ); + } + return 0; +} + + +/*-------------------------------------------------------------------------*/ + +static int spidev_probe(struct spi_device *spi) +{ + dma_addr_t phy_addr; + + /* 1. 记录spi_device */ + oled = spi; + + /* 2. 注册字符设备 */ + major = register_chrdev(0, "100ask_oled", &spidev_fops); + spidev_class = class_create(THIS_MODULE, "100ask_oled"); + device_create(spidev_class, NULL, MKDEV(major, 0), NULL, "100ask_oled"); + + /* 3. 获得GPIO引脚 */ + dc_gpio = gpiod_get(&spi->dev, "dc", 0); + + + + /* A. 分配fb_info */ + myfb_info = framebuffer_alloc(0, NULL); + + /* B. 设置fb_info */ + /* B.1 var : LCD分辨率、颜色格式 */ + myfb_info->var.xres_virtual = myfb_info->var.xres = 128; + myfb_info->var.yres_virtual = myfb_info->var.yres = 64; + + myfb_info->var.bits_per_pixel = 1; /* rgb565 */ + + /* B.2 fix */ + strcpy(myfb_info->fix.id, "100ask_oled"); + myfb_info->fix.smem_len = myfb_info->var.xres * myfb_info->var.yres * myfb_info->var.bits_per_pixel / 8; + + myfb_info->flags |= FBINFO_MODULE; /* 禁止显示LOGO */ + + + /* fb的虚拟地址 */ + myfb_info->screen_base = dma_alloc_wc(NULL, myfb_info->fix.smem_len, &phy_addr, + GFP_KERNEL); + myfb_info->fix.smem_start = phy_addr; /* fb的物理地址 */ + + myfb_info->fix.type = FB_TYPE_PACKED_PIXELS; + myfb_info->fix.visual = FB_VISUAL_MONO10; + + myfb_info->fix.line_length = myfb_info->var.xres * myfb_info->var.bits_per_pixel / 8; + + /* c. fbops */ + myfb_info->fbops = &myfb_ops; + myfb_info->pseudo_palette = pseudo_palette; + + + /* C. 注册fb_info */ + register_framebuffer(myfb_info); + + /* D. 创建内核线程 */ + oled_buf = kmalloc(1024, GFP_KERNEL); + dc_pin_init(); + oled_init(); + + oled_thread = kthread_run(oled_thread_func, NULL, "oled_kthead"); + + return 0; +} + +static int spidev_remove(struct spi_device *spi) +{ + kthread_stop(oled_thread); + kfree(oled_buf); + + /* A. 反注册fb_info */ + unregister_framebuffer(myfb_info); + + /* B. 释放内存 */ + dma_free_wc(NULL, myfb_info->fix.smem_len, myfb_info->screen_base, + myfb_info->fix.smem_start); + + /* C. 释放fb_info */ + framebuffer_release(myfb_info); + + gpiod_put(dc_gpio); + + /* 反注册字符设 */ + device_destroy(spidev_class, MKDEV(major, 0)); + class_destroy(spidev_class); + unregister_chrdev(major, "100ask_oled"); + + return 0; +} + +static struct spi_driver spidev_spi_driver = { + .driver = { + .name = "100ask_spi_oled_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"); + diff --git a/IMX6ULL/source/11_SPI/10_oled_framebuffer_ok/spi_oled.c b/IMX6ULL/source/11_SPI/10_oled_framebuffer_ok/spi_oled.c new file mode 100644 index 0000000..98810de --- /dev/null +++ b/IMX6ULL/source/11_SPI/10_oled_framebuffer_ok/spi_oled.c @@ -0,0 +1,241 @@ +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include + +#include "font.h" + +#define OLED_IOC_INIT 123 +#define OLED_IOC_SET_POS 124 + + +//为0 表示命令,为1表示数据 +#define OLED_CMD 0 +#define OLED_DATA 1 + +static int fd_spidev; +static int dc_pin_num; + + +void OLED_DIsp_Set_Pos(int x, int y); + +void oled_write_datas(const unsigned char *buf, int len) +{ + write(fd_spidev, buf, len); +} + + + +/********************************************************************** + * 函数名称: OLED_DIsp_Clear + * 功能描述: 整个屏幕显示数据清0 + * 输入参数:无 + * 输出参数: 无 + * 返 回 值: + * 修改日期 版本号 修改人 修改内容 + * ----------------------------------------------- + * 2020/03/15 V1.0 芯晓 创建 + ***********************************************************************/ +void OLED_DIsp_Clear(void) +{ + unsigned char x, y; + char buf[128]; + + memset(buf, 0, 128); + + for (y = 0; y < 8; y++) + { + OLED_DIsp_Set_Pos(0, y); + oled_write_datas(buf, 128); + } +} + +/********************************************************************** + * 函数名称: OLED_DIsp_All + * 功能描述: 整个屏幕显示全部点亮,可以用于检查坏点 + * 输入参数:无 + * 输出参数:无 + * 返 回 值: + * 修改日期 版本号 修改人 修改内容 + * ----------------------------------------------- + * 2020/03/15 V1.0 芯晓 创建 + ***********************************************************************/ +void OLED_DIsp_All(void) +{ + unsigned char x, y; + char buf[128]; + + memset(buf, 0xff, 128); + + for (y = 0; y < 8; y++) + { + OLED_DIsp_Set_Pos(0, y); + oled_write_datas(buf, 128); + } + + + +} + +//坐标设置 +/********************************************************************** + * 函数名称: OLED_DIsp_Set_Pos + * 功能描述:设置要显示的位置 + * 输入参数:@ x :要显示的column address + @y :要显示的page address + * 输出参数: 无 + * 返 回 值: + * 修改日期 版本号 修改人 修改内容 + * ----------------------------------------------- + * 2020/03/15 V1.0 芯晓 创建 + ***********************************************************************/ +void OLED_DIsp_Set_Pos(int x, int y) +{ + ioctl(fd_spidev, OLED_IOC_SET_POS, x | (y << 8)); +} +/********************************************************************** + * 函数名称: OLED_DIsp_Char + * 功能描述:在某个位置显示字符 1-9 + * 输入参数:@ x :要显示的column address + @y :要显示的page address + @c :要显示的字符的ascii码 + * 输出参数: 无 + * 返 回 值: + * 修改日期 版本号 修改人 修改内容 + * ----------------------------------------------- + * 2020/03/15 V1.0 芯晓 创建 +***********************************************************************/ +void OLED_DIsp_Char(int x, int y, unsigned char c) +{ + int i = 0; + /* 得到字模 */ + const unsigned char *dots = oled_asc2_8x16[c - ' ']; + + /* 发给OLED */ + OLED_DIsp_Set_Pos(x, y); + /* 发出8字节数据 */ + //for (i = 0; i < 8; i++) + // oled_write_cmd_data(dots[i], OLED_DATA); + oled_write_datas(&dots[0], 8); + + OLED_DIsp_Set_Pos(x, y+1); + /* 发出8字节数据 */ + //for (i = 0; i < 8; i++) + //oled_write_cmd_data(dots[i+8], OLED_DATA); + oled_write_datas(&dots[8], 8); +} + + +/********************************************************************** + * 函数名称: OLED_DIsp_String + * 功能描述: 在指定位置显示字符串 + * 输入参数:@ x :要显示的column address + @y :要显示的page address + @str :要显示的字符串 + * 输出参数: 无 + * 返 回 值: 无 + * 修改日期 版本号 修改人 修改内容 + * ----------------------------------------------- + * 2020/03/15 V1.0 芯晓 创建 +***********************************************************************/ +void OLED_DIsp_String(int x, int y, char *str) +{ + unsigned char j=0; + while (str[j]) + { + OLED_DIsp_Char(x, y, str[j]);//显示单个字符 + x += 8; + if(x > 127) + { + x = 0; + y += 2; + }//移动显示位置 + j++; + } +} +/********************************************************************** + * 函数名称: OLED_DIsp_CHinese + * 功能描述:在指定位置显示汉字 + * 输入参数:@ x :要显示的column address + @y :要显示的page address + @chr :要显示的汉字,三个汉字“百问网”中选择一个 + * 输出参数: 无 + * 返 回 值: 无 + * 修改日期 版本号 修改人 修改内容 + * ----------------------------------------------- + * 2020/03/15 V1.0 芯晓 创建 + ***********************************************************************/ + +void OLED_DIsp_CHinese(unsigned char x,unsigned char y,unsigned char no) +{ + unsigned char t,adder=0; + OLED_DIsp_Set_Pos(x,y); + for(t=0;t<16;t++) + {//显示上半截字符 + oled_write_datas(&hz_1616[no][t*2], 1); + adder+=1; + } + OLED_DIsp_Set_Pos(x,y+1); + for(t=0;t<16;t++) + {//显示下半截字符 + oled_write_datas(&hz_1616[no][t*2+1], 1); + adder+=1; + } +} +/********************************************************************** + * 函数名称: OLED_DIsp_Test + * 功能描述: 整个屏幕显示测试 + * 输入参数:无 + * 输出参数: 无 + * 返 回 值: 无 + * 修改日期 版本号 修改人 修改内容 + * ----------------------------------------------- + * 2020/03/15 V1.0 芯晓 创建 + ***********************************************************************/ +void OLED_DIsp_Test(void) +{ + int i; + + OLED_DIsp_String(0, 0, "wiki.100ask.net"); + OLED_DIsp_String(0, 2, "book.100ask.net"); + OLED_DIsp_String(0, 4, "bbs.100ask.net"); + + for(i = 0; i < 3; i++) + { //显示汉字 百问网 + OLED_DIsp_CHinese(32+i*16, 6, i); + } +} + +/* spi_oled /dev/100ask_oled */ +int main(int argc, char **argv) +{ + if (argc != 2) + { + printf("Usage: %s /dev/100ask_oled\n", argv[0]); + return -1; + } + + fd_spidev = open(argv[1], O_RDWR); + if (fd_spidev < 0) { + printf("open %s err\n", argv[1]); + return -1; + } + + + ioctl(fd_spidev, OLED_IOC_INIT); + + OLED_DIsp_Clear(); + + OLED_DIsp_Test(); + + return 0; +} + diff --git a/IMX6ULL/source/11_SPI/10_oled_framebuffer_ok/spi_oled.h b/IMX6ULL/source/11_SPI/10_oled_framebuffer_ok/spi_oled.h new file mode 100644 index 0000000..692b290 --- /dev/null +++ b/IMX6ULL/source/11_SPI/10_oled_framebuffer_ok/spi_oled.h @@ -0,0 +1,17 @@ +#ifndef _OLED_H_ +#define _OLED_H_ + +void oled_write_cmd_data(unsigned char uc_data,unsigned char uc_cmd); +int oled_init(void); +int oled_fill_data(unsigned char fill_Data); +void OLED_DIsp_Clear(void); +void OLED_DIsp_All(void); +void OLED_DIsp_Set_Pos(int x, int y); +void OLED_DIsp_Char(int x, int y, unsigned char c); +void OLED_DIsp_String(int x, int y, char *str); +void OLED_DIsp_CHinese(unsigned char x,unsigned char y,unsigned char no); +void OLED_DIsp_Test(); + + +#endif + diff --git a/README.md b/README.md index 532868f..9cfc445 100644 --- a/README.md +++ b/README.md @@ -571,6 +571,12 @@ git clone https://e.coding.net/weidongshan/linux/doc_and_source_for_drivers.git 16_使用Framebuffer改造OLED驱动 ``` +* 2021.05.13 发布"SPI子系统": + + ```shell + 17_OLED_Framebuffer驱动_上机实验 + ``` + diff --git a/STM32MP157/doc_pic/11_SPI/17_OLED_Framebuffer驱动_上机实验.md b/STM32MP157/doc_pic/11_SPI/17_OLED_Framebuffer驱动_上机实验.md new file mode 100644 index 0000000..2134516 --- /dev/null +++ b/STM32MP157/doc_pic/11_SPI/17_OLED_Framebuffer驱动_上机实验.md @@ -0,0 +1,242 @@ +# OLED_Framebuffer驱动_上机实验 # + +* 源码: + + ![image-20220509140847199](pic/79_src_for_framebuffer_ok.png) + +* DMA参考文章:https://www.kernel.org/doc/html/latest/core-api/dma-api-howto.html + +## 1. 硬件 + +### 1.1 原理图 + +IMX6ULL: + +![image-20220316120535482](pic/47_imx6ull_oled_sch.png) + +STM32MP157: + +![image-20220316120427564](pic/48_stm32mp157_oled_sch.png) + + + +原理图: + +![image-20220314114847437](pic/49_oled_sch.png) + + + +### 1.2 连接 + +无论是使用IMX6ULL开发板还是STM32MP157开发板,都有类似的扩展板。把OLED模块接到扩展板的SPI_A插座上,如下: + +![image-20220314115446373](pic/50_oled_on_extend_brd.png) + + + + + +## 2. 编写设备树 + +### 2.1.1 IMX6ULL + +![](pic/44_imx6ull_pro_extend_spi_a.png) + +DC引脚使用GPIO4_20,也需在设备树里指定。 + +设备树如下: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"; + + oled: oled { + compatible = "100ask,oled"; + reg = <0>; + spi-max-frequency = <10000000>; + dc-gpios = <&gpio4 20 GPIO_ACTIVE_HIGH>; + }; +}; +``` + + + + + +### 2.1.2 STM32MP157 + +![](pic/45_stm32mp157_pro_extend_spi_a.png) + +DC引脚使用GPIOA_13,也需要在设备树里指定。 + +设备树如下:`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>; + }; + oled: oled@1{ + compatible = "100ask,oled"; + spi-max-frequency = <10000000>; + reg = <1>; + dc-gpios = <&gpioa 13 GPIO_ACTIVE_HIGH>; + }; +}; +``` + + + +## 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` + + ![](pic/46_boot_mount.png) + +* 更新设备树 + + ```shell + [root@100ask:~]# cp /mnt/stm32mp157c-100ask-512d-lcd-v1.dtb /boot/ + [root@100ask:~]# sync + ``` + +* 重启开发板 + + + +## 4. 编译OLED驱动 + +```shell +cd 10_oled_framebuffer_ok +make +``` + + + +## 5. 编译APP + +```shell +cd 10_oled_framebuffer_ok/03_freetype_show_font_angle +make +``` + + + + + +## 6. 上机实验 + +```shell +1. 安装驱动程序, 确定新出现哪个设备节点 +ls /dev/fb* +insmod oled_drv.ko +ls /dev/fb* + +2. 在IMX6ULL上运行测试程序 +./freetype_show_font_angle /dev/fb2 ./simsun.ttc 0 20 + +2. 在STM32MP157上运行测试程序 +./freetype_show_font_angle /dev/fb1 ./simsun.ttc 0 20 +``` + diff --git a/STM32MP157/doc_pic/11_SPI/17_OLED_Framebuffer驱动_上机实验.tif b/STM32MP157/doc_pic/11_SPI/17_OLED_Framebuffer驱动_上机实验.tif new file mode 100644 index 0000000..0fc2d72 Binary files /dev/null and b/STM32MP157/doc_pic/11_SPI/17_OLED_Framebuffer驱动_上机实验.tif differ diff --git a/STM32MP157/doc_pic/11_SPI/pic/79_src_for_framebuffer_ok.png b/STM32MP157/doc_pic/11_SPI/pic/79_src_for_framebuffer_ok.png new file mode 100644 index 0000000..eeded1a Binary files /dev/null and b/STM32MP157/doc_pic/11_SPI/pic/79_src_for_framebuffer_ok.png differ diff --git a/STM32MP157/source/A7/11_SPI/10_oled_framebuffer_ok/03_freetype_show_font_angle/Makefile b/STM32MP157/source/A7/11_SPI/10_oled_framebuffer_ok/03_freetype_show_font_angle/Makefile new file mode 100644 index 0000000..681a0d9 --- /dev/null +++ b/STM32MP157/source/A7/11_SPI/10_oled_framebuffer_ok/03_freetype_show_font_angle/Makefile @@ -0,0 +1,18 @@ + +# 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个环境变量不一定相同, +# 请参考各开发板的高级用户使用手册 + + +all: + $(CROSS_COMPILE)gcc -I /home/book/100ask_stm32mp157_pro-sdk/ToolChain/arm-buildroot-linux-gnueabihf_sdk-buildroot/arm-buildroot-linux-gnueabihf/sysroot/usr/include/freetype2 -o freetype_show_font_angle freetype_show_font_angle.c -lm -lfreetype + +clean: + rm -rf *.o freetype_show_font_angle + + + diff --git a/STM32MP157/source/A7/11_SPI/10_oled_framebuffer_ok/03_freetype_show_font_angle/freetype_show_font_angle.c b/STM32MP157/source/A7/11_SPI/10_oled_framebuffer_ok/03_freetype_show_font_angle/freetype_show_font_angle.c new file mode 100644 index 0000000..fb2b994 --- /dev/null +++ b/STM32MP157/source/A7/11_SPI/10_oled_framebuffer_ok/03_freetype_show_font_angle/freetype_show_font_angle.c @@ -0,0 +1,210 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include FT_FREETYPE_H +#include FT_GLYPH_H + +int fd_fb; +struct fb_var_screeninfo var; /* Current var */ +int screen_size; +unsigned char *fbmem; +unsigned int line_width; +unsigned int pixel_width; + + +/********************************************************************** + * 函数名称: lcd_put_pixel + * 功能描述: 在LCD指定位置上输出指定颜色(描点) + * 输入参数: x坐标,y坐标,颜色 + * 输出参数: 无 + * 返 回 值: 会 + * 修改日期 版本号 修改人 修改内容 + * ----------------------------------------------- + * 2020/05/12 V1.0 zh(angenao) 创建 + ***********************************************************************/ +void lcd_put_pixel(int x, int y, unsigned int color) +{ + unsigned char *pen_8 = fbmem+y*line_width+x*pixel_width; + unsigned short *pen_16; + unsigned int *pen_32; + + unsigned int red, green, blue; + int bit; + + pen_16 = (unsigned short *)pen_8; + pen_32 = (unsigned int *)pen_8; + + switch (var.bits_per_pixel) + { + case 1: + { + pen_8 = fbmem+y*line_width+x/8; + bit = x & 7; + if (color) + *pen_8 = *pen_8 | (1<> 16) & 0xff; + green = (color >> 8) & 0xff; + blue = (color >> 0) & 0xff; + color = ((red >> 3) << 11) | ((green >> 2) << 5) | (blue >> 3); + *pen_16 = color; + break; + } + case 32: + { + *pen_32 = color; + break; + } + default: + { + printf("can't surport %dbpp\n", var.bits_per_pixel); + break; + } + } +} + +/********************************************************************** + * 函数名称: draw_bitmap + * 功能描述: 根据bitmap位图,在LCD指定位置显示汉字 + * 输入参数: x坐标,y坐标,位图指针 + * 输出参数: 无 + * 返 回 值: 无 + * 修改日期 版本号 修改人 修改内容 + * ----------------------------------------------- + * 2020/05/12 V1.0 zh(angenao) 创建 + ***********************************************************************/ +void +draw_bitmap( FT_Bitmap* bitmap, + FT_Int x, + FT_Int y) +{ + FT_Int i, j, p, q; + FT_Int x_max = x + bitmap->width; + FT_Int y_max = y + bitmap->rows; + + //printf("x = %d, y = %d\n", x, y); + + for ( j = y, q = 0; j < y_max; j++, q++ ) + { + for ( i = x, p = 0; i < x_max; i++, p++ ) + { + if ( i < 0 || j < 0 || + i >= var.xres || j >= var.yres ) + continue; + + //image[j][i] |= bitmap->buffer[q * bitmap->width + p]; + lcd_put_pixel(i, j, bitmap->buffer[q * bitmap->width + p]); + } + } +} + + +int main(int argc, char **argv) +{ + wchar_t *chinese_str = L"繁"; + + FT_Library library; + FT_Face face; + int error; + FT_Vector pen; + FT_GlyphSlot slot; + int font_size = 24; + FT_Matrix matrix; /* transformation matrix */ + double angle; + + if (argc < 3) + { + printf("Usage : %s [font_size]\n", argv[0]); + return -1; + } + + angle = ( 1.0* strtoul(argv[3], NULL, 0) / 360 ) * 3.14159 * 2; /* use 25 degrees */ + + if (argc == 5) + font_size = strtoul(argv[4], NULL, 0); + + fd_fb = open(argv[1], O_RDWR); + if (fd_fb < 0) + { + printf("can't open %s\n", argv[1]); + return -1; + } + + if (ioctl(fd_fb, FBIOGET_VSCREENINFO, &var)) + { + printf("can't get var\n"); + return -1; + } + + line_width = var.xres * var.bits_per_pixel / 8; + pixel_width = var.bits_per_pixel / 8; + screen_size = var.xres * var.yres * var.bits_per_pixel / 8; + fbmem = (unsigned char *)mmap(NULL , screen_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd_fb, 0); + if (fbmem == (unsigned char *)-1) + { + printf("can't mmap\n"); + return -1; + } + + /* 清屏: 全部设为黑色 */ + memset(fbmem, 0, screen_size); + + /* 显示矢量字体 */ + error = FT_Init_FreeType( &library ); /* initialize library */ + /* error handling omitted */ + + error = FT_New_Face( library, argv[2], 0, &face ); /* create face object */ + /* error handling omitted */ + slot = face->glyph; + + FT_Set_Pixel_Sizes(face, font_size, 0); + + /* 确定座标: + */ + pen.x = 0; + pen.y = 0; + + /* set up matrix */ + matrix.xx = (FT_Fixed)( cos( angle ) * 0x10000L ); + matrix.xy = (FT_Fixed)(-sin( angle ) * 0x10000L ); + matrix.yx = (FT_Fixed)( sin( angle ) * 0x10000L ); + matrix.yy = (FT_Fixed)( cos( angle ) * 0x10000L ); + + /* set transformation */ + FT_Set_Transform( face, &matrix, &pen); + + /* load glyph image into the slot (erase previous one) */ + error = FT_Load_Char( face, chinese_str[0], FT_LOAD_RENDER ); + if (error) + { + printf("FT_Load_Char error\n"); + return -1; + } + + draw_bitmap( &slot->bitmap, + var.xres/2, + var.yres/2); + + return 0; +} + diff --git a/STM32MP157/source/A7/11_SPI/10_oled_framebuffer_ok/03_freetype_show_font_angle/simsun.ttc b/STM32MP157/source/A7/11_SPI/10_oled_framebuffer_ok/03_freetype_show_font_angle/simsun.ttc new file mode 100644 index 0000000..23d5c4a Binary files /dev/null and b/STM32MP157/source/A7/11_SPI/10_oled_framebuffer_ok/03_freetype_show_font_angle/simsun.ttc differ diff --git a/STM32MP157/source/A7/11_SPI/10_oled_framebuffer_ok/Makefile b/STM32MP157/source/A7/11_SPI/10_oled_framebuffer_ok/Makefile new file mode 100644 index 0000000..bffc069 --- /dev/null +++ b/STM32MP157/source/A7/11_SPI/10_oled_framebuffer_ok/Makefile @@ -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 spi_oled spi_oled.c + +clean: + make -C $(KERN_DIR) M=`pwd` modules clean + rm -rf modules.order spi_oled + +obj-m += oled_drv.o + + diff --git a/STM32MP157/source/A7/11_SPI/10_oled_framebuffer_ok/font.h b/STM32MP157/source/A7/11_SPI/10_oled_framebuffer_ok/font.h new file mode 100644 index 0000000..5698917 --- /dev/null +++ b/STM32MP157/source/A7/11_SPI/10_oled_framebuffer_ok/font.h @@ -0,0 +1,106 @@ +#ifndef _FONT_H_ +#define _FONT_H_ +const unsigned char oled_asc2_8x16[95][16]= +{ + {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},// 0 + {0x00,0x00,0x00,0xF8,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x33,0x30,0x00,0x00,0x00},//!1 + {0x00,0x10,0x0C,0x06,0x10,0x0C,0x06,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},//"2 + {0x40,0xC0,0x78,0x40,0xC0,0x78,0x40,0x00,0x04,0x3F,0x04,0x04,0x3F,0x04,0x04,0x00},//#3 + {0x00,0x70,0x88,0xFC,0x08,0x30,0x00,0x00,0x00,0x18,0x20,0xFF,0x21,0x1E,0x00,0x00},//$4 + {0xF0,0x08,0xF0,0x00,0xE0,0x18,0x00,0x00,0x00,0x21,0x1C,0x03,0x1E,0x21,0x1E,0x00},//%5 + {0x00,0xF0,0x08,0x88,0x70,0x00,0x00,0x00,0x1E,0x21,0x23,0x24,0x19,0x27,0x21,0x10},//&6 + {0x10,0x16,0x0E,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},//'7 + {0x00,0x00,0x00,0xE0,0x18,0x04,0x02,0x00,0x00,0x00,0x00,0x07,0x18,0x20,0x40,0x00},//(8 + {0x00,0x02,0x04,0x18,0xE0,0x00,0x00,0x00,0x00,0x40,0x20,0x18,0x07,0x00,0x00,0x00},//)9 + {0x40,0x40,0x80,0xF0,0x80,0x40,0x40,0x00,0x02,0x02,0x01,0x0F,0x01,0x02,0x02,0x00},//*10 + {0x00,0x00,0x00,0xF0,0x00,0x00,0x00,0x00,0x01,0x01,0x01,0x1F,0x01,0x01,0x01,0x00},//+11 + {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x80,0xB0,0x70,0x00,0x00,0x00,0x00,0x00},//,12 + {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x01,0x01,0x01,0x01,0x01,0x01},//-13 + {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x30,0x30,0x00,0x00,0x00,0x00,0x00},//.14 + {0x00,0x00,0x00,0x00,0x80,0x60,0x18,0x04,0x00,0x60,0x18,0x06,0x01,0x00,0x00,0x00},///15 + {0x00,0xE0,0x10,0x08,0x08,0x10,0xE0,0x00,0x00,0x0F,0x10,0x20,0x20,0x10,0x0F,0x00},//016 + {0x00,0x10,0x10,0xF8,0x00,0x00,0x00,0x00,0x00,0x20,0x20,0x3F,0x20,0x20,0x00,0x00},//117 + {0x00,0x70,0x08,0x08,0x08,0x88,0x70,0x00,0x00,0x30,0x28,0x24,0x22,0x21,0x30,0x00},//218 + {0x00,0x30,0x08,0x88,0x88,0x48,0x30,0x00,0x00,0x18,0x20,0x20,0x20,0x11,0x0E,0x00},//319 + {0x00,0x00,0xC0,0x20,0x10,0xF8,0x00,0x00,0x00,0x07,0x04,0x24,0x24,0x3F,0x24,0x00},//420 + {0x00,0xF8,0x08,0x88,0x88,0x08,0x08,0x00,0x00,0x19,0x21,0x20,0x20,0x11,0x0E,0x00},//521 + {0x00,0xE0,0x10,0x88,0x88,0x18,0x00,0x00,0x00,0x0F,0x11,0x20,0x20,0x11,0x0E,0x00},//622 + {0x00,0x38,0x08,0x08,0xC8,0x38,0x08,0x00,0x00,0x00,0x00,0x3F,0x00,0x00,0x00,0x00},//723 + {0x00,0x70,0x88,0x08,0x08,0x88,0x70,0x00,0x00,0x1C,0x22,0x21,0x21,0x22,0x1C,0x00},//824 + {0x00,0xE0,0x10,0x08,0x08,0x10,0xE0,0x00,0x00,0x00,0x31,0x22,0x22,0x11,0x0F,0x00},//925 + {0x00,0x00,0x00,0xC0,0xC0,0x00,0x00,0x00,0x00,0x00,0x00,0x30,0x30,0x00,0x00,0x00},//:26 + {0x00,0x00,0x00,0x80,0x00,0x00,0x00,0x00,0x00,0x00,0x80,0x60,0x00,0x00,0x00,0x00},//;27 + {0x00,0x00,0x80,0x40,0x20,0x10,0x08,0x00,0x00,0x01,0x02,0x04,0x08,0x10,0x20,0x00},//<28 + {0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x00,0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x00},//=29 + {0x00,0x08,0x10,0x20,0x40,0x80,0x00,0x00,0x00,0x20,0x10,0x08,0x04,0x02,0x01,0x00},//>30 + {0x00,0x70,0x48,0x08,0x08,0x08,0xF0,0x00,0x00,0x00,0x00,0x30,0x36,0x01,0x00,0x00},//?31 + {0xC0,0x30,0xC8,0x28,0xE8,0x10,0xE0,0x00,0x07,0x18,0x27,0x24,0x23,0x14,0x0B,0x00},//@32 + {0x00,0x00,0xC0,0x38,0xE0,0x00,0x00,0x00,0x20,0x3C,0x23,0x02,0x02,0x27,0x38,0x20},//A33 + {0x08,0xF8,0x88,0x88,0x88,0x70,0x00,0x00,0x20,0x3F,0x20,0x20,0x20,0x11,0x0E,0x00},//B34 + {0xC0,0x30,0x08,0x08,0x08,0x08,0x38,0x00,0x07,0x18,0x20,0x20,0x20,0x10,0x08,0x00},//C35 + {0x08,0xF8,0x08,0x08,0x08,0x10,0xE0,0x00,0x20,0x3F,0x20,0x20,0x20,0x10,0x0F,0x00},//D36 + {0x08,0xF8,0x88,0x88,0xE8,0x08,0x10,0x00,0x20,0x3F,0x20,0x20,0x23,0x20,0x18,0x00},//E37 + {0x08,0xF8,0x88,0x88,0xE8,0x08,0x10,0x00,0x20,0x3F,0x20,0x00,0x03,0x00,0x00,0x00},//F38 + {0xC0,0x30,0x08,0x08,0x08,0x38,0x00,0x00,0x07,0x18,0x20,0x20,0x22,0x1E,0x02,0x00},//G39 + {0x08,0xF8,0x08,0x00,0x00,0x08,0xF8,0x08,0x20,0x3F,0x21,0x01,0x01,0x21,0x3F,0x20},//H40 + {0x00,0x08,0x08,0xF8,0x08,0x08,0x00,0x00,0x00,0x20,0x20,0x3F,0x20,0x20,0x00,0x00},//I41 + {0x00,0x00,0x08,0x08,0xF8,0x08,0x08,0x00,0xC0,0x80,0x80,0x80,0x7F,0x00,0x00,0x00},//J42 + {0x08,0xF8,0x88,0xC0,0x28,0x18,0x08,0x00,0x20,0x3F,0x20,0x01,0x26,0x38,0x20,0x00},//K43 + {0x08,0xF8,0x08,0x00,0x00,0x00,0x00,0x00,0x20,0x3F,0x20,0x20,0x20,0x20,0x30,0x00},//L44 + {0x08,0xF8,0xF8,0x00,0xF8,0xF8,0x08,0x00,0x20,0x3F,0x00,0x3F,0x00,0x3F,0x20,0x00},//M45 + {0x08,0xF8,0x30,0xC0,0x00,0x08,0xF8,0x08,0x20,0x3F,0x20,0x00,0x07,0x18,0x3F,0x00},//N46 + {0xE0,0x10,0x08,0x08,0x08,0x10,0xE0,0x00,0x0F,0x10,0x20,0x20,0x20,0x10,0x0F,0x00},//O47 + {0x08,0xF8,0x08,0x08,0x08,0x08,0xF0,0x00,0x20,0x3F,0x21,0x01,0x01,0x01,0x00,0x00},//P48 + {0xE0,0x10,0x08,0x08,0x08,0x10,0xE0,0x00,0x0F,0x18,0x24,0x24,0x38,0x50,0x4F,0x00},//Q49 + {0x08,0xF8,0x88,0x88,0x88,0x88,0x70,0x00,0x20,0x3F,0x20,0x00,0x03,0x0C,0x30,0x20},//R50 + {0x00,0x70,0x88,0x08,0x08,0x08,0x38,0x00,0x00,0x38,0x20,0x21,0x21,0x22,0x1C,0x00},//S51 + {0x18,0x08,0x08,0xF8,0x08,0x08,0x18,0x00,0x00,0x00,0x20,0x3F,0x20,0x00,0x00,0x00},//T52 + {0x08,0xF8,0x08,0x00,0x00,0x08,0xF8,0x08,0x00,0x1F,0x20,0x20,0x20,0x20,0x1F,0x00},//U53 + {0x08,0x78,0x88,0x00,0x00,0xC8,0x38,0x08,0x00,0x00,0x07,0x38,0x0E,0x01,0x00,0x00},//V54 + {0xF8,0x08,0x00,0xF8,0x00,0x08,0xF8,0x00,0x03,0x3C,0x07,0x00,0x07,0x3C,0x03,0x00},//W55 + {0x08,0x18,0x68,0x80,0x80,0x68,0x18,0x08,0x20,0x30,0x2C,0x03,0x03,0x2C,0x30,0x20},//X56 + {0x08,0x38,0xC8,0x00,0xC8,0x38,0x08,0x00,0x00,0x00,0x20,0x3F,0x20,0x00,0x00,0x00},//Y57 + {0x10,0x08,0x08,0x08,0xC8,0x38,0x08,0x00,0x20,0x38,0x26,0x21,0x20,0x20,0x18,0x00},//Z58 + {0x00,0x00,0x00,0xFE,0x02,0x02,0x02,0x00,0x00,0x00,0x00,0x7F,0x40,0x40,0x40,0x00},//[59 + {0x00,0x0C,0x30,0xC0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x06,0x38,0xC0,0x00},//\60 + {0x00,0x02,0x02,0x02,0xFE,0x00,0x00,0x00,0x00,0x40,0x40,0x40,0x7F,0x00,0x00,0x00},//]61 + {0x00,0x00,0x04,0x02,0x02,0x02,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},//^62 + {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80},//_63 + {0x00,0x02,0x02,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},//`64 + {0x00,0x00,0x80,0x80,0x80,0x80,0x00,0x00,0x00,0x19,0x24,0x22,0x22,0x22,0x3F,0x20},//a65 + {0x08,0xF8,0x00,0x80,0x80,0x00,0x00,0x00,0x00,0x3F,0x11,0x20,0x20,0x11,0x0E,0x00},//b66 + {0x00,0x00,0x00,0x80,0x80,0x80,0x00,0x00,0x00,0x0E,0x11,0x20,0x20,0x20,0x11,0x00},//c67 + {0x00,0x00,0x00,0x80,0x80,0x88,0xF8,0x00,0x00,0x0E,0x11,0x20,0x20,0x10,0x3F,0x20},//d68 + {0x00,0x00,0x80,0x80,0x80,0x80,0x00,0x00,0x00,0x1F,0x22,0x22,0x22,0x22,0x13,0x00},//e69 + {0x00,0x80,0x80,0xF0,0x88,0x88,0x88,0x18,0x00,0x20,0x20,0x3F,0x20,0x20,0x00,0x00},//f70 + {0x00,0x00,0x80,0x80,0x80,0x80,0x80,0x00,0x00,0x6B,0x94,0x94,0x94,0x93,0x60,0x00},//g71 + {0x08,0xF8,0x00,0x80,0x80,0x80,0x00,0x00,0x20,0x3F,0x21,0x00,0x00,0x20,0x3F,0x20},//h72 + {0x00,0x80,0x98,0x98,0x00,0x00,0x00,0x00,0x00,0x20,0x20,0x3F,0x20,0x20,0x00,0x00},//i73 + {0x00,0x00,0x00,0x80,0x98,0x98,0x00,0x00,0x00,0xC0,0x80,0x80,0x80,0x7F,0x00,0x00},//j74 + {0x08,0xF8,0x00,0x00,0x80,0x80,0x80,0x00,0x20,0x3F,0x24,0x02,0x2D,0x30,0x20,0x00},//k75 + {0x00,0x08,0x08,0xF8,0x00,0x00,0x00,0x00,0x00,0x20,0x20,0x3F,0x20,0x20,0x00,0x00},//l76 + {0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x00,0x20,0x3F,0x20,0x00,0x3F,0x20,0x00,0x3F},//m77 + {0x80,0x80,0x00,0x80,0x80,0x80,0x00,0x00,0x20,0x3F,0x21,0x00,0x00,0x20,0x3F,0x20},//n78 + {0x00,0x00,0x80,0x80,0x80,0x80,0x00,0x00,0x00,0x1F,0x20,0x20,0x20,0x20,0x1F,0x00},//o79 + {0x80,0x80,0x00,0x80,0x80,0x00,0x00,0x00,0x80,0xFF,0xA1,0x20,0x20,0x11,0x0E,0x00},//p80 + {0x00,0x00,0x00,0x80,0x80,0x80,0x80,0x00,0x00,0x0E,0x11,0x20,0x20,0xA0,0xFF,0x80},//q81 + {0x80,0x80,0x80,0x00,0x80,0x80,0x80,0x00,0x20,0x20,0x3F,0x21,0x20,0x00,0x01,0x00},//r82 + {0x00,0x00,0x80,0x80,0x80,0x80,0x80,0x00,0x00,0x33,0x24,0x24,0x24,0x24,0x19,0x00},//s83 + {0x00,0x80,0x80,0xE0,0x80,0x80,0x00,0x00,0x00,0x00,0x00,0x1F,0x20,0x20,0x00,0x00},//t84 + {0x80,0x80,0x00,0x00,0x00,0x80,0x80,0x00,0x00,0x1F,0x20,0x20,0x20,0x10,0x3F,0x20},//u85 + {0x80,0x80,0x80,0x00,0x00,0x80,0x80,0x80,0x00,0x01,0x0E,0x30,0x08,0x06,0x01,0x00},//v86 + {0x80,0x80,0x00,0x80,0x00,0x80,0x80,0x80,0x0F,0x30,0x0C,0x03,0x0C,0x30,0x0F,0x00},//w87 + {0x00,0x80,0x80,0x00,0x80,0x80,0x80,0x00,0x00,0x20,0x31,0x2E,0x0E,0x31,0x20,0x00},//x88 + {0x80,0x80,0x80,0x00,0x00,0x80,0x80,0x80,0x80,0x81,0x8E,0x70,0x18,0x06,0x01,0x00},//y89 + {0x00,0x80,0x80,0x80,0x80,0x80,0x80,0x00,0x00,0x21,0x30,0x2C,0x22,0x21,0x30,0x00},//z90 + {0x00,0x00,0x00,0x00,0x80,0x7C,0x02,0x02,0x00,0x00,0x00,0x00,0x00,0x3F,0x40,0x40},//{91 + {0x00,0x00,0x00,0x00,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0x00,0x00,0x00},//|92 + {0x00,0x02,0x02,0x7C,0x80,0x00,0x00,0x00,0x00,0x40,0x40,0x3F,0x00,0x00,0x00,0x00},//}93 + {0x00,0x06,0x01,0x01,0x02,0x02,0x04,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},//~94 +}; +const unsigned char hz_1616[][32]={ +{0x02,0x00,0x02,0x00,0xE2,0xFF,0x22,0x42,0x22,0x42,0x32,0x42,0x2A,0x42,0x26,0x42,0x22,0x42,0x22,0x42,0x22,0x42,0x22,0x42,0xE2,0xFF,0x02,0x00,0x02,0x00,0x00,0x00},/*"百",0*/ +{0x00,0x00,0xF8,0xFF,0x01,0x00,0x02,0x00,0x00,0x00,0xE2,0x1F,0x22,0x08,0x22,0x08,0x22,0x08,0xE2,0x1F,0x02,0x00,0x02,0x40,0x02,0x80,0xFE,0x7F,0x00,0x00,0x00,0x00},/*"问",1*/ +{0x00,0x00,0xFE,0xFF,0x02,0x10,0x22,0x08,0x42,0x06,0x82,0x01,0x72,0x0E,0x02,0x10,0x22,0x08,0x42,0x06,0x82,0x01,0x72,0x4E,0x02,0x80,0xFE,0x7F,0x00,0x00,0x00,0x00},/*"网",2*/ +}; +#endif diff --git a/STM32MP157/source/A7/11_SPI/10_oled_framebuffer_ok/oled_drv.c b/STM32MP157/source/A7/11_SPI/10_oled_framebuffer_ok/oled_drv.c new file mode 100644 index 0000000..8fd8e6b --- /dev/null +++ b/STM32MP157/source/A7/11_SPI/10_oled_framebuffer_ok/oled_drv.c @@ -0,0 +1,491 @@ +/* + * Simple synchronous userspace interface to SPI devices + * + * Copyright (C) 2006 SWAPP + * Andrea Paterniani + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include + +#include +#include +#include + +#define OLED_IOC_INIT 123 +#define OLED_IOC_SET_POS 124 + +//为0 表示命令,为1表示数据 +#define OLED_CMD 0 +#define OLED_DATA 1 + +static struct fb_info *myfb_info; + +static unsigned int pseudo_palette[16]; + +static struct task_struct *oled_thread; + +static unsigned char *oled_buf; //[1024]; + +/* from pxafb.c */ +static inline unsigned int chan_to_field(unsigned int chan, + struct fb_bitfield *bf) +{ + chan &= 0xffff; + chan >>= 16 - bf->length; + return chan << bf->offset; +} + +static int mylcd_setcolreg(unsigned regno, + unsigned red, unsigned green, unsigned blue, + unsigned transp, struct fb_info *info) +{ + unsigned int val; + + /* dprintk("setcol: regno=%d, rgb=%d,%d,%d\n", + regno, red, green, blue); */ + + switch (info->fix.visual) { + case FB_VISUAL_TRUECOLOR: + /* true-colour, use pseudo-palette */ + + if (regno < 16) { + u32 *pal = info->pseudo_palette; + + val = chan_to_field(red, &info->var.red); + val |= chan_to_field(green, &info->var.green); + val |= chan_to_field(blue, &info->var.blue); + + pal[regno] = val; + } + break; + + default: + return 1; /* unknown type */ + } + + return 0; +} + + +static struct fb_ops myfb_ops = { + .owner = THIS_MODULE, + .fb_setcolreg = mylcd_setcolreg, + .fb_fillrect = cfb_fillrect, + .fb_copyarea = cfb_copyarea, + .fb_imageblit = cfb_imageblit, +}; + + + +/*-------------------------------------------------------------------------*/ + +static struct spi_device *oled; +static int major; +static struct gpio_desc *dc_gpio; + +static void dc_pin_init(void) +{ + gpiod_direction_output(dc_gpio, 1); +} + +static void oled_set_dc_pin(int val) +{ + gpiod_set_value(dc_gpio, val); +} + +static void spi_write_datas(const unsigned char *buf, int len) +{ + spi_write(oled, buf, len); +} + + +/********************************************************************** + * 函数名称: oled_write_cmd + * 功能描述: oled向特定地址写入数据或者命令 + * 输入参数:@uc_data :要写入的数据 + @uc_cmd:为1则表示写入数据,为0表示写入命令 + * 输出参数:无 + * 返 回 值: 无 + * 修改日期 版本号 修改人 修改内容 + * ----------------------------------------------- + * 2020/03/04 V1.0 芯晓 创建 + ***********************************************************************/ +static void oled_write_cmd_data(unsigned char uc_data,unsigned char uc_cmd) +{ + if(uc_cmd==0) + { + oled_set_dc_pin(0); + } + else + { + oled_set_dc_pin(1);//拉高,表示写入数据 + } + spi_write_datas(&uc_data, 1);//写入 +} + + +/********************************************************************** + * 函数名称: oled_init + * 功能描述: oled_init的初始化,包括SPI控制器得初始化 + * 输入参数:无 + * 输出参数: 初始化的结果 + * 返 回 值: 成功则返回0,否则返回-1 + * 修改日期 版本号 修改人 修改内容 + * ----------------------------------------------- + * 2020/03/15 V1.0 芯晓 创建 + ***********************************************************************/ +static int oled_init(void) +{ + oled_write_cmd_data(0xae,OLED_CMD);//关闭显示 + + oled_write_cmd_data(0x00,OLED_CMD);//设置 lower column address + oled_write_cmd_data(0x10,OLED_CMD);//设置 higher column address + + oled_write_cmd_data(0x40,OLED_CMD);//设置 display start line + + oled_write_cmd_data(0xB0,OLED_CMD);//设置page address + + oled_write_cmd_data(0x81,OLED_CMD);// contract control + oled_write_cmd_data(0x66,OLED_CMD);//128 + + oled_write_cmd_data(0xa1,OLED_CMD);//设置 segment remap + + oled_write_cmd_data(0xa6,OLED_CMD);//normal /reverse + + oled_write_cmd_data(0xa8,OLED_CMD);//multiple ratio + oled_write_cmd_data(0x3f,OLED_CMD);//duty = 1/64 + + oled_write_cmd_data(0xc8,OLED_CMD);//com scan direction + + oled_write_cmd_data(0xd3,OLED_CMD);//set displat offset + oled_write_cmd_data(0x00,OLED_CMD);// + + oled_write_cmd_data(0xd5,OLED_CMD);//set osc division + oled_write_cmd_data(0x80,OLED_CMD);// + + oled_write_cmd_data(0xd9,OLED_CMD);//ser pre-charge period + oled_write_cmd_data(0x1f,OLED_CMD);// + + oled_write_cmd_data(0xda,OLED_CMD);//set com pins + oled_write_cmd_data(0x12,OLED_CMD);// + + oled_write_cmd_data(0xdb,OLED_CMD);//set vcomh + oled_write_cmd_data(0x30,OLED_CMD);// + + oled_write_cmd_data(0x8d,OLED_CMD);//set charge pump disable + oled_write_cmd_data(0x14,OLED_CMD);// + + oled_write_cmd_data(0xaf,OLED_CMD);//set dispkay on + + return 0; +} + +//坐标设置 +/********************************************************************** + * 函数名称: OLED_DIsp_Set_Pos + * 功能描述:设置要显示的位置 + * 输入参数:@ x :要显示的column address + @y :要显示的page address + * 输出参数: 无 + * 返 回 值: + * 修改日期 版本号 修改人 修改内容 + * ----------------------------------------------- + * 2020/03/15 V1.0 芯晓 创建 + ***********************************************************************/ +static void OLED_DIsp_Set_Pos(int x, int y) +{ oled_write_cmd_data(0xb0+y,OLED_CMD); + oled_write_cmd_data((x&0x0f),OLED_CMD); + oled_write_cmd_data(((x&0xf0)>>4)|0x10,OLED_CMD); +} + + +static long +spidev_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) +{ + int x, y; + + /* 根据cmd操作硬件 */ + switch (cmd) + { + case OLED_IOC_INIT: /* init */ + { + dc_pin_init(); + oled_init(); + break; + } + + case OLED_IOC_SET_POS: /* set pos */ + { + x = arg & 0xff; + y = (arg >> 8) & 0xff; + OLED_DIsp_Set_Pos(x, y); + break; + } + + } + + return 0; +} + +static ssize_t +spidev_write(struct file *filp, const char __user *buf, + size_t count, loff_t *f_pos) +{ + char *ker_buf; + int err; + + ker_buf = kmalloc(count, GFP_KERNEL); + err = copy_from_user(ker_buf, buf, count); + + oled_set_dc_pin(1);//拉高,表示写入数据 + spi_write_datas(ker_buf, count); + kfree(ker_buf); + return count; +} + +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. + */ + .write = spidev_write, + .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,oled" }, + {}, +}; + + + +static int oled_thread_func(void *param) +{ + unsigned char *p[8]; + unsigned char data[8]; + int i; + int j; + int line; + int bit; + unsigned char byte; + unsigned char *fb = myfb_info->screen_base; + int k; + + while (!kthread_should_stop()) + { + /* 1. 从Framebuffer得到数据 */ + /* 2. 转换格式 */ + k = 0; + for (i = 0; i < 8; i++) + { + for (line = 0; line < 8; line++) + p[line] = &fb[i*128 + line * 16]; + + for (j = 0; j < 16; j++) + { + for (line = 0; line < 8; line++) + { + data[line] = *p[line]; + p[line] += 1; + } + + for (bit = 0; bit < 8; bit++) + { + byte = (((data[0]>>bit) & 1) << 0) | + (((data[1]>>bit) & 1) << 1) | + (((data[2]>>bit) & 1) << 2) | + (((data[3]>>bit) & 1) << 3) | + (((data[4]>>bit) & 1) << 4) | + (((data[5]>>bit) & 1) << 5) | + (((data[6]>>bit) & 1) << 6) | + (((data[7]>>bit) & 1) << 7); + + oled_buf[k++] = byte; + } + + } + } + + + /* 3. 通过SPI发送给OLED */ + for (i = 0; i < 8; i++) + { + OLED_DIsp_Set_Pos(0, i); + oled_set_dc_pin(1); + spi_write_datas(&oled_buf[i*128], 128); + } + + + /* 4. 休眠一会 */ + schedule_timeout_interruptible(HZ); + } + return 0; +} + + +/*-------------------------------------------------------------------------*/ + +static int spidev_probe(struct spi_device *spi) +{ + dma_addr_t phy_addr; + + /* 1. 记录spi_device */ + oled = spi; + + /* 2. 注册字符设备 */ + major = register_chrdev(0, "100ask_oled", &spidev_fops); + spidev_class = class_create(THIS_MODULE, "100ask_oled"); + device_create(spidev_class, NULL, MKDEV(major, 0), NULL, "100ask_oled"); + + /* 3. 获得GPIO引脚 */ + dc_gpio = gpiod_get(&spi->dev, "dc", 0); + + + + /* A. 分配fb_info */ + myfb_info = framebuffer_alloc(0, NULL); + + /* B. 设置fb_info */ + /* B.1 var : LCD分辨率、颜色格式 */ + myfb_info->var.xres_virtual = myfb_info->var.xres = 128; + myfb_info->var.yres_virtual = myfb_info->var.yres = 64; + + myfb_info->var.bits_per_pixel = 1; /* rgb565 */ + + /* B.2 fix */ + strcpy(myfb_info->fix.id, "100ask_oled"); + myfb_info->fix.smem_len = myfb_info->var.xres * myfb_info->var.yres * myfb_info->var.bits_per_pixel / 8; + + //myfb_info->flags |= FBINFO_MODULE; /* 禁止显示LOGO */ + + + /* fb的虚拟地址 */ + dma_set_coherent_mask(&spi->dev, DMA_BIT_MASK(32)); + myfb_info->screen_base = dma_alloc_wc(&spi->dev, myfb_info->fix.smem_len, &phy_addr, + GFP_KERNEL); + myfb_info->fix.smem_start = phy_addr; /* fb的物理地址 */ + + myfb_info->fix.type = FB_TYPE_PACKED_PIXELS; + myfb_info->fix.visual = FB_VISUAL_MONO10; + + myfb_info->fix.line_length = myfb_info->var.xres * myfb_info->var.bits_per_pixel / 8; + + /* c. fbops */ + myfb_info->fbops = &myfb_ops; + myfb_info->pseudo_palette = pseudo_palette; + + + /* C. 注册fb_info */ + register_framebuffer(myfb_info); + + /* D. 创建内核线程 */ + oled_buf = kmalloc(1024, GFP_KERNEL); + dc_pin_init(); + oled_init(); + + oled_thread = kthread_run(oled_thread_func, NULL, "oled_kthead"); + + return 0; +} + +static int spidev_remove(struct spi_device *spi) +{ + kthread_stop(oled_thread); + kfree(oled_buf); + + /* A. 反注册fb_info */ + unregister_framebuffer(myfb_info); + + /* B. 释放内存 */ + dma_free_wc(&spi->dev, myfb_info->fix.smem_len, myfb_info->screen_base, + myfb_info->fix.smem_start); + + /* C. 释放fb_info */ + framebuffer_release(myfb_info); + + gpiod_put(dc_gpio); + + /* 反注册字符设 */ + device_destroy(spidev_class, MKDEV(major, 0)); + class_destroy(spidev_class); + unregister_chrdev(major, "100ask_oled"); + + return 0; +} + +static struct spi_driver spidev_spi_driver = { + .driver = { + .name = "100ask_spi_oled_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"); + diff --git a/STM32MP157/source/A7/11_SPI/10_oled_framebuffer_ok/spi_oled.c b/STM32MP157/source/A7/11_SPI/10_oled_framebuffer_ok/spi_oled.c new file mode 100644 index 0000000..98810de --- /dev/null +++ b/STM32MP157/source/A7/11_SPI/10_oled_framebuffer_ok/spi_oled.c @@ -0,0 +1,241 @@ +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include + +#include "font.h" + +#define OLED_IOC_INIT 123 +#define OLED_IOC_SET_POS 124 + + +//为0 表示命令,为1表示数据 +#define OLED_CMD 0 +#define OLED_DATA 1 + +static int fd_spidev; +static int dc_pin_num; + + +void OLED_DIsp_Set_Pos(int x, int y); + +void oled_write_datas(const unsigned char *buf, int len) +{ + write(fd_spidev, buf, len); +} + + + +/********************************************************************** + * 函数名称: OLED_DIsp_Clear + * 功能描述: 整个屏幕显示数据清0 + * 输入参数:无 + * 输出参数: 无 + * 返 回 值: + * 修改日期 版本号 修改人 修改内容 + * ----------------------------------------------- + * 2020/03/15 V1.0 芯晓 创建 + ***********************************************************************/ +void OLED_DIsp_Clear(void) +{ + unsigned char x, y; + char buf[128]; + + memset(buf, 0, 128); + + for (y = 0; y < 8; y++) + { + OLED_DIsp_Set_Pos(0, y); + oled_write_datas(buf, 128); + } +} + +/********************************************************************** + * 函数名称: OLED_DIsp_All + * 功能描述: 整个屏幕显示全部点亮,可以用于检查坏点 + * 输入参数:无 + * 输出参数:无 + * 返 回 值: + * 修改日期 版本号 修改人 修改内容 + * ----------------------------------------------- + * 2020/03/15 V1.0 芯晓 创建 + ***********************************************************************/ +void OLED_DIsp_All(void) +{ + unsigned char x, y; + char buf[128]; + + memset(buf, 0xff, 128); + + for (y = 0; y < 8; y++) + { + OLED_DIsp_Set_Pos(0, y); + oled_write_datas(buf, 128); + } + + + +} + +//坐标设置 +/********************************************************************** + * 函数名称: OLED_DIsp_Set_Pos + * 功能描述:设置要显示的位置 + * 输入参数:@ x :要显示的column address + @y :要显示的page address + * 输出参数: 无 + * 返 回 值: + * 修改日期 版本号 修改人 修改内容 + * ----------------------------------------------- + * 2020/03/15 V1.0 芯晓 创建 + ***********************************************************************/ +void OLED_DIsp_Set_Pos(int x, int y) +{ + ioctl(fd_spidev, OLED_IOC_SET_POS, x | (y << 8)); +} +/********************************************************************** + * 函数名称: OLED_DIsp_Char + * 功能描述:在某个位置显示字符 1-9 + * 输入参数:@ x :要显示的column address + @y :要显示的page address + @c :要显示的字符的ascii码 + * 输出参数: 无 + * 返 回 值: + * 修改日期 版本号 修改人 修改内容 + * ----------------------------------------------- + * 2020/03/15 V1.0 芯晓 创建 +***********************************************************************/ +void OLED_DIsp_Char(int x, int y, unsigned char c) +{ + int i = 0; + /* 得到字模 */ + const unsigned char *dots = oled_asc2_8x16[c - ' ']; + + /* 发给OLED */ + OLED_DIsp_Set_Pos(x, y); + /* 发出8字节数据 */ + //for (i = 0; i < 8; i++) + // oled_write_cmd_data(dots[i], OLED_DATA); + oled_write_datas(&dots[0], 8); + + OLED_DIsp_Set_Pos(x, y+1); + /* 发出8字节数据 */ + //for (i = 0; i < 8; i++) + //oled_write_cmd_data(dots[i+8], OLED_DATA); + oled_write_datas(&dots[8], 8); +} + + +/********************************************************************** + * 函数名称: OLED_DIsp_String + * 功能描述: 在指定位置显示字符串 + * 输入参数:@ x :要显示的column address + @y :要显示的page address + @str :要显示的字符串 + * 输出参数: 无 + * 返 回 值: 无 + * 修改日期 版本号 修改人 修改内容 + * ----------------------------------------------- + * 2020/03/15 V1.0 芯晓 创建 +***********************************************************************/ +void OLED_DIsp_String(int x, int y, char *str) +{ + unsigned char j=0; + while (str[j]) + { + OLED_DIsp_Char(x, y, str[j]);//显示单个字符 + x += 8; + if(x > 127) + { + x = 0; + y += 2; + }//移动显示位置 + j++; + } +} +/********************************************************************** + * 函数名称: OLED_DIsp_CHinese + * 功能描述:在指定位置显示汉字 + * 输入参数:@ x :要显示的column address + @y :要显示的page address + @chr :要显示的汉字,三个汉字“百问网”中选择一个 + * 输出参数: 无 + * 返 回 值: 无 + * 修改日期 版本号 修改人 修改内容 + * ----------------------------------------------- + * 2020/03/15 V1.0 芯晓 创建 + ***********************************************************************/ + +void OLED_DIsp_CHinese(unsigned char x,unsigned char y,unsigned char no) +{ + unsigned char t,adder=0; + OLED_DIsp_Set_Pos(x,y); + for(t=0;t<16;t++) + {//显示上半截字符 + oled_write_datas(&hz_1616[no][t*2], 1); + adder+=1; + } + OLED_DIsp_Set_Pos(x,y+1); + for(t=0;t<16;t++) + {//显示下半截字符 + oled_write_datas(&hz_1616[no][t*2+1], 1); + adder+=1; + } +} +/********************************************************************** + * 函数名称: OLED_DIsp_Test + * 功能描述: 整个屏幕显示测试 + * 输入参数:无 + * 输出参数: 无 + * 返 回 值: 无 + * 修改日期 版本号 修改人 修改内容 + * ----------------------------------------------- + * 2020/03/15 V1.0 芯晓 创建 + ***********************************************************************/ +void OLED_DIsp_Test(void) +{ + int i; + + OLED_DIsp_String(0, 0, "wiki.100ask.net"); + OLED_DIsp_String(0, 2, "book.100ask.net"); + OLED_DIsp_String(0, 4, "bbs.100ask.net"); + + for(i = 0; i < 3; i++) + { //显示汉字 百问网 + OLED_DIsp_CHinese(32+i*16, 6, i); + } +} + +/* spi_oled /dev/100ask_oled */ +int main(int argc, char **argv) +{ + if (argc != 2) + { + printf("Usage: %s /dev/100ask_oled\n", argv[0]); + return -1; + } + + fd_spidev = open(argv[1], O_RDWR); + if (fd_spidev < 0) { + printf("open %s err\n", argv[1]); + return -1; + } + + + ioctl(fd_spidev, OLED_IOC_INIT); + + OLED_DIsp_Clear(); + + OLED_DIsp_Test(); + + return 0; +} + diff --git a/STM32MP157/source/A7/11_SPI/10_oled_framebuffer_ok/spi_oled.h b/STM32MP157/source/A7/11_SPI/10_oled_framebuffer_ok/spi_oled.h new file mode 100644 index 0000000..692b290 --- /dev/null +++ b/STM32MP157/source/A7/11_SPI/10_oled_framebuffer_ok/spi_oled.h @@ -0,0 +1,17 @@ +#ifndef _OLED_H_ +#define _OLED_H_ + +void oled_write_cmd_data(unsigned char uc_data,unsigned char uc_cmd); +int oled_init(void); +int oled_fill_data(unsigned char fill_Data); +void OLED_DIsp_Clear(void); +void OLED_DIsp_All(void); +void OLED_DIsp_Set_Pos(int x, int y); +void OLED_DIsp_Char(int x, int y, unsigned char c); +void OLED_DIsp_String(int x, int y, char *str); +void OLED_DIsp_CHinese(unsigned char x,unsigned char y,unsigned char no); +void OLED_DIsp_Test(); + + +#endif +