实验介绍
GC9A01是一款小巧(1.28寸)、彩色(分辨率为 240 * 240 RGB)圆形TFT屏幕,它采用4线 SPI的控制方式,电源供电电压为3.3V,有7个控制引脚;本次实验将使用它来验证龙芯SOC的SPI通信流程,在用户态下移植和编写SPI设备驱动
模块连接
VCC接Pin2,GND接Pin1,SCL接Pin21,SDA接Pin27,DC接Pin16,CS接Pin23,RST接Pin18
SPI接口分布:SCLK -> GPIO64, CS -> GPIO67, MISO -> GPIO65, MOSI -> GPIO66
设备树
板载了2路SPI总线接口,SPI0是给norflash(QSPI)通信用的,只有SPI2是空闲的
&spi0{
status = "okay";
pinctrl-names = "default";
pinctrl-0 = <&spi0_6bit_f0>,<&spi0_6bit_f1>;
norflash@0 {
reg = <0>;
spi-rx-bus-width = <4>;
spi-max-frequency = <25000000>;
#address-cells = <1>;
#size-cells = <1>;
partitions {
compatible = "fixed-partitions";
#address-cells = <1>;
#size-cells = <1>;
boot@0 {
label = "boot";
reg = <0x0000000 0x100000>;
/*read-only;*/
};
kernel@100000 {
label = "kernel";
reg = <0x100000 0x600000>;
};
rootfs@700000 {
label = "rootfs";
reg = <0x700000 0x1000000>;
};
};
};
};
&spi2{
status = "okay";
pinctrl-names = "default";
pinctrl-0 = <&spi2_pin>;
};
在/dev目录下可以查看到spi2总线对应的设备节点:spidev1.0
可以尝试短接SPI2的MOSI和MISO引脚进行自发自收测试,检查rx_dat是否接收到相同的数据
unsigned char tx_dat[] = {'h', 'e', 'l', 'l', 'o', ',', 'w', 'o', 'r', 'l', 'd'};
unsigned char rx_dat[sizeof(tx)] = {0};
struct spi_ioc_transfer tr = {
.tx_buf = (unsigned long)tx_dat,
.rx_buf = (unsigned long)rx_dat,
.len = sizeof(tx_buf),
.delay_usecs = 0,
.speed_hz = 500000,
.bits_per_word = 8,
};
ioctl(fd, SPI_IOC_MESSAGE(1), &tr);
驱动代码
lcd_init.h
定义spi设备及lcd初始化的接口
#ifndef __LCD_INIT_H
#define __LCD_INIT_H
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <linux/spi/spidev.h>
#define USE_HORIZONTAL 0
#define LCD_W 240
#define LCD_H 240
typedef unsigned int u32;
typedef unsigned short u16;
typedef unsigned char u8;
void LCD_Writ_Bus(u8 dat);
void LCD_WR_DATA8(u8 dat);
void LCD_WR_DATA(u16 dat);
void LCD_WR_REG(u8 dat);
void LCD_Address_Set(u16 x1,u16 y1,u16 x2,u16 y2);
void LCD_Init(void);
void closeLCD();
#endif
lcd_init.c
spi设备及lcd初始化的代码实现
#include "lcd_init.h"
#define GPIO_PATH "/sys/class/gpio"
#define LCD_DC_Clr() { write(dc_fd, "0", 1); }
#define LCD_DC_Set() { write(dc_fd, "1", 1); }
#define LCD_RES_Clr() { write(res_fd, "0", 1); }
#define LCD_RES_Set() { write(res_fd, "1", 1); }
static int spi_fd = -1;
static int dc_fd = -1;
static int res_fd = -1;
int init_gpio(int gpio)
{
char path[64];
int fd = -1;
// export
{
sprintf(path, "%s/export", GPIO_PATH);
fd = open(path, O_WRONLY);
if (fd < 0) {
perror("open export for writing");
return -1;
}
write(fd, &gpio, sizeof(gpio));
close(fd);
}
// direction
{
sprintf(path, "%s/gpio%d/direction", GPIO_PATH, gpio);
fd = open(path, O_RDWR);
if (fd < 0) {
perror("open direction for reading and writing");
return -1;
}
write(fd, "out", 3);
close(fd);
}
// value
{
sprintf(path, "%s/gpio%d/value", GPIO_PATH, gpio);
fd = open(path, O_RDWR);
if (fd < 0) {
perror("open value for reading and writing");
return -1;
}
write(fd, "1", 1);
}
return fd;
}
void closeLCD()
{
close(dc_fd);
close(res_fd);
close(spi_fd);
}
void LCD_Writ_Bus(u8 dat)
{
int ret = 0;
unsigned char tx[] = {dat};
unsigned char rx[sizeof(tx)] = {0};
struct spi_ioc_transfer tr = {
.tx_buf = (unsigned long)tx,
.rx_buf = (unsigned long)rx,
.len = sizeof(tx),
};
ret = ioctl(spi_fd, SPI_IOC_MESSAGE(1), &tr);
if (ret < 1) {
perror("can't send spi message");
}
}
void LCD_WR_DATA8(u8 dat)
{
LCD_Writ_Bus(dat);
}
void LCD_WR_DATA(u16 dat)
{
LCD_Writ_Bus(dat>>8);
LCD_Writ_Bus(dat);
}
void LCD_WR_REG(u8 dat)
{
LCD_DC_Clr();
LCD_Writ_Bus(dat);
LCD_DC_Set();
}
void LCD_Address_Set(u16 x1,u16 y1,u16 x2,u16 y2)
{
LCD_WR_REG(0x2a);
LCD_WR_DATA(x1);
LCD_WR_DATA(x2);
LCD_WR_REG(0x2b);
LCD_WR_DATA(y1);
LCD_WR_DATA(y2);
LCD_WR_REG(0x2c);
}
void delay_ms(int ms)
{
usleep(ms * 1000);
}
void LCD_Init(void)
{
int ret;
// init pins
{
dc_fd = init_gpio(48);
res_fd = init_gpio(49);
spi_fd = open("/dev/spidev1.0", O_RDWR);
if (spi_fd < 0) {
perror("open");
return ;
}
unsigned char mode = SPI_MODE_0;
unsigned char bits = 8;
unsigned int speed = 25000000;
ret = ioctl(spi_fd, SPI_IOC_WR_MODE, &mode);
ret = ioctl(spi_fd, SPI_IOC_WR_BITS_PER_WORD, &bits);
ret = ioctl(spi_fd, SPI_IOC_WR_MAX_SPEED_HZ, &speed);
}
LCD_RES_Clr();
delay_ms(100);
LCD_RES_Set();
delay_ms(100);
LCD_WR_REG(0xEF);
LCD_WR_REG(0xEB);
LCD_WR_DATA8(0x14);
LCD_WR_REG(0xFE);
LCD_WR_REG(0xEF);
LCD_WR_REG(0xEB);
LCD_WR_DATA8(0x14);
LCD_WR_REG(0x84);
LCD_WR_DATA8(0x40);
LCD_WR_REG(0x85);
LCD_WR_DATA8(0xFF);
LCD_WR_REG(0x86);
LCD_WR_DATA8(0xFF);
LCD_WR_REG(0x87);
LCD_WR_DATA8(0xFF);
LCD_WR_REG(0x88);
LCD_WR_DATA8(0x0A);
LCD_WR_REG(0x89);
LCD_WR_DATA8(0x21);
LCD_WR_REG(0x8A);
LCD_WR_DATA8(0x00);
LCD_WR_REG(0x8B);
LCD_WR_DATA8(0x80);
LCD_WR_REG(0x8C);
LCD_WR_DATA8(0x01);
LCD_WR_REG(0x8D);
LCD_WR_DATA8(0x01);
LCD_WR_REG(0x8E);
LCD_WR_DATA8(0xFF);
LCD_WR_REG(0x8F);
LCD_WR_DATA8(0xFF);
LCD_WR_REG(0xB6);
LCD_WR_DATA8(0x00);
LCD_WR_DATA8(0x20);
LCD_WR_REG(0x36);
if(USE_HORIZONTAL==0) LCD_WR_DATA8(0x08);
else if(USE_HORIZONTAL==1) LCD_WR_DATA8(0xC8);
else if(USE_HORIZONTAL==2) LCD_WR_DATA8(0x68);
else LCD_WR_DATA8(0xA8);
LCD_WR_REG(0x3A);
LCD_WR_DATA8(0x05);
LCD_WR_REG(0x90);
LCD_WR_DATA8(0x08);
LCD_WR_DATA8(0x08);
LCD_WR_DATA8(0x08);
LCD_WR_DATA8(0x08);
LCD_WR_REG(0xBD);
LCD_WR_DATA8(0x06);
LCD_WR_REG(0xBC);
LCD_WR_DATA8(0x00);
LCD_WR_REG(0xFF);
LCD_WR_DATA8(0x60);
LCD_WR_DATA8(0x01);
LCD_WR_DATA8(0x04);
LCD_WR_REG(0xC3);
LCD_WR_DATA8(0x13);
LCD_WR_REG(0xC4);
LCD_WR_DATA8(0x13);
LCD_WR_REG(0xC9);
LCD_WR_DATA8(0x22);
LCD_WR_REG(0xBE);
LCD_WR_DATA8(0x11);
LCD_WR_REG(0xE1);
LCD_WR_DATA8(0x10);
LCD_WR_DATA8(0x0E);
LCD_WR_REG(0xDF);
LCD_WR_DATA8(0x21);
LCD_WR_DATA8(0x0c);
LCD_WR_DATA8(0x02);
LCD_WR_REG(0xF0);
LCD_WR_DATA8(0x45);
LCD_WR_DATA8(0x09);
LCD_WR_DATA8(0x08);
LCD_WR_DATA8(0x08);
LCD_WR_DATA8(0x26);
LCD_WR_DATA8(0x2A);
LCD_WR_REG(0xF1);
LCD_WR_DATA8(0x43);
LCD_WR_DATA8(0x70);
LCD_WR_DATA8(0x72);
LCD_WR_DATA8(0x36);
LCD_WR_DATA8(0x37);
LCD_WR_DATA8(0x6F);
LCD_WR_REG(0xF2);
LCD_WR_DATA8(0x45);
LCD_WR_DATA8(0x09);
LCD_WR_DATA8(0x08);
LCD_WR_DATA8(0x08);
LCD_WR_DATA8(0x26);
LCD_WR_DATA8(0x2A);
LCD_WR_REG(0xF3);
LCD_WR_DATA8(0x43);
LCD_WR_DATA8(0x70);
LCD_WR_DATA8(0x72);
LCD_WR_DATA8(0x36);
LCD_WR_DATA8(0x37);
LCD_WR_DATA8(0x6F);
LCD_WR_REG(0xED);
LCD_WR_DATA8(0x1B);
LCD_WR_DATA8(0x0B);
LCD_WR_REG(0xAE);
LCD_WR_DATA8(0x77);
LCD_WR_REG(0xCD);
LCD_WR_DATA8(0x63);
LCD_WR_REG(0x70);
LCD_WR_DATA8(0x07);
LCD_WR_DATA8(0x07);
LCD_WR_DATA8(0x04);
LCD_WR_DATA8(0x0E);
LCD_WR_DATA8(0x0F);
LCD_WR_DATA8(0x09);
LCD_WR_DATA8(0x07);
LCD_WR_DATA8(0x08);
LCD_WR_DATA8(0x03);
LCD_WR_REG(0xE8);
LCD_WR_DATA8(0x34);
LCD_WR_REG(0x62);
LCD_WR_DATA8(0x18);
LCD_WR_DATA8(0x0D);
LCD_WR_DATA8(0x71);
LCD_WR_DATA8(0xED);
LCD_WR_DATA8(0x70);
LCD_WR_DATA8(0x70);
LCD_WR_DATA8(0x18);
LCD_WR_DATA8(0x0F);
LCD_WR_DATA8(0x71);
LCD_WR_DATA8(0xEF);
LCD_WR_DATA8(0x70);
LCD_WR_DATA8(0x70);
LCD_WR_REG(0x63);
LCD_WR_DATA8(0x18);
LCD_WR_DATA8(0x11);
LCD_WR_DATA8(0x71);
LCD_WR_DATA8(0xF1);
LCD_WR_DATA8(0x70);
LCD_WR_DATA8(0x70);
LCD_WR_DATA8(0x18);
LCD_WR_DATA8(0x13);
LCD_WR_DATA8(0x71);
LCD_WR_DATA8(0xF3);
LCD_WR_DATA8(0x70);
LCD_WR_DATA8(0x70);
LCD_WR_REG(0x64);
LCD_WR_DATA8(0x28);
LCD_WR_DATA8(0x29);
LCD_WR_DATA8(0xF1);
LCD_WR_DATA8(0x01);
LCD_WR_DATA8(0xF1);
LCD_WR_DATA8(0x00);
LCD_WR_DATA8(0x07);
LCD_WR_REG(0x66);
LCD_WR_DATA8(0x3C);
LCD_WR_DATA8(0x00);
LCD_WR_DATA8(0xCD);
LCD_WR_DATA8(0x67);
LCD_WR_DATA8(0x45);
LCD_WR_DATA8(0x45);
LCD_WR_DATA8(0x10);
LCD_WR_DATA8(0x00);
LCD_WR_DATA8(0x00);
LCD_WR_DATA8(0x00);
LCD_WR_REG(0x67);
LCD_WR_DATA8(0x00);
LCD_WR_DATA8(0x3C);
LCD_WR_DATA8(0x00);
LCD_WR_DATA8(0x00);
LCD_WR_DATA8(0x00);
LCD_WR_DATA8(0x01);
LCD_WR_DATA8(0x54);
LCD_WR_DATA8(0x10);
LCD_WR_DATA8(0x32);
LCD_WR_DATA8(0x98);
LCD_WR_REG(0x74);
LCD_WR_DATA8(0x10);
LCD_WR_DATA8(0x85);
LCD_WR_DATA8(0x80);
LCD_WR_DATA8(0x00);
LCD_WR_DATA8(0x00);
LCD_WR_DATA8(0x4E);
LCD_WR_DATA8(0x00);
LCD_WR_REG(0x98);
LCD_WR_DATA8(0x3e);
LCD_WR_DATA8(0x07);
LCD_WR_REG(0x35);
LCD_WR_REG(0x21);
LCD_WR_REG(0x11);
delay_ms(120);
LCD_WR_REG(0x29);
delay_ms(20);
}
main.c
这里实现了一个圆形波纹的动画效果,代码根据GC9A01官方例程移植而来
#include <stdio.h>
#include "lcd_init.h"
void LCD_Fill(u16 xsta,u16 ysta,u16 xend,u16 yend,u16 color)
{
u16 i,j;
LCD_Address_Set(xsta,ysta,xend-1,yend-1);
for(i=ysta;i<yend;i++)
{
for(j=xsta;j<xend;j++)
{
LCD_WR_DATA(color);
}
}
}
void LCD_DrawPoint(u16 x,u16 y,u16 color)
{
LCD_Address_Set(x,y,x,y);
LCD_WR_DATA(color);
}
void Draw_Circle(u16 x0,u16 y0,u8 r,u16 color)
{
int a,b;
a=0;b=r;
while(a<=b)
{
LCD_DrawPoint(x0-b,y0-a,color);
LCD_DrawPoint(x0+b,y0-a,color);
LCD_DrawPoint(x0-a,y0+b,color);
LCD_DrawPoint(x0-a,y0-b,color);
LCD_DrawPoint(x0+b,y0+a,color);
LCD_DrawPoint(x0+a,y0-b,color);
LCD_DrawPoint(x0+a,y0+b,color);
LCD_DrawPoint(x0-b,y0+a,color);
a++;
if((a*a+b*b)>(r*r))
{
b--;
}
}
}
int main()
{
int j = 0;
LCD_Init();
LCD_Fill(0, 0, 240, 240, BLACK);
while (1)
{
for(int i = 0; i < 240; i += 10) {
Draw_Circle(120, 120, i, 400 * j);
}
if(j++ > 65536 / 400) {
j = 0;
}
usleep(1000000);
}
closeLCD();
return 0;
}
实验效果
圆形波纹动画
参考
GC9A01官方例程地址:https://gitee.com/meta-mcu/gc9a01-tft.git