跳到主要内容

SPI 驱动

结构体

定义 SPI 模式

typedef enum {
SPI_IO_SINGLE = 0x00,
SPI_IO_DUAL_RX,
SPI_IO_QUAD_RX,
SPI_IO_QUAD_IO,
} spi_io_mode_t;

定义 SPI 时钟寄存器

typedef struct spi_clk_reg {
uint32_t ccu_base;
uint32_t spi_clk_reg_offest;
uint32_t spi_bgr_reg_offset;
} spi_clk_reg_t;

定义 SPI 描述,包括基地址,控制器号,GPIO配置

typedef struct {
uint32_t base;
uint8_t id;
spi_clk_reg_t clk_reg;
uint32_t clk_rate;
gpio_mux_t gpio_cs;
gpio_mux_t gpio_sck;
gpio_mux_t gpio_miso;
gpio_mux_t gpio_mosi;
gpio_mux_t gpio_wp;
gpio_mux_t gpio_hold;
} sunxi_spi_t;

API 接口

sunxi_spi_init

int sunxi_spi_init(sunxi_spi_t *spi);

初始化具有指定配置的Sunxi SPI控制器。

  • spi:指向Sunxi SPI控制器结构的指针。
  • 返回值:如果成功,则返回0;如果失败,则返回错误代码。

sunxi_spi_disable

void sunxi_spi_disable(sunxi_spi_t *spi);

禁用Sunxi SPI控制器。

  • spi:指向Sunxi SPI控制器结构的指针。

sunxi_spi_transfer

int sunxi_spi_transfer(sunxi_spi_t *spi, spi_io_mode_t mode, void *txbuf, uint32_t txlen, void *rxbuf, uint32_t rxlen);

使用Sunxi SPI控制器执行SPI传输。

  • spi:指向Sunxi SPI控制器结构的指针。
  • mode:传输时要使用的SPI IO模式(例如,SPI_IO_MODE_SINGLE,SPI_IO_MODE_DUAL,SPI_IO_MODE_QUAD)。
  • txbuf:指向要传输的数据的缓冲区的指针。
  • txlen:要传输的数据的长度(以字节为单位)。
  • rxbuf:用于存储接收到的数据的缓冲区的指针。
  • rxlen:要接收的数据的长度(以字节为单位)。
  • 返回值:如果成功,则返回0;如果失败,则返回错误代码。

使用示例

首先定义 SPI 使用的设备

sunxi_spi_t sunxi_spi0 = {
.base = SUNXI_SPI0_BASE,
.id = 0,
.clk_rate = 75 * 1000 * 1000,
.gpio_cs = {GPIO_PIN(GPIO_PORTC, 1), GPIO_PERIPH_MUX4},
.gpio_sck = {GPIO_PIN(GPIO_PORTC, 0), GPIO_PERIPH_MUX4},
.gpio_mosi = {GPIO_PIN(GPIO_PORTC, 2), GPIO_PERIPH_MUX4},
.gpio_miso = {GPIO_PIN(GPIO_PORTC, 3), GPIO_PERIPH_MUX4},
.gpio_wp = {GPIO_PIN(GPIO_PORTC, 4), GPIO_PERIPH_MUX4},
.gpio_hold = {GPIO_PIN(GPIO_PORTC, 5), GPIO_PERIPH_MUX4},
};

然后初始化 SPI

if (sunxi_spi_init(&sunxi_spi0_lcd) != 0) {
printk_error("SPI: init failed\n");
}

然后发送数据

if (sunxi_spi_transfer(&sunxi_spi0_lcd, SPI_IO_SINGLE, tx, 1, 0, 0) < 0)
printk_error("SPI: SPI Xfer error!\n");

如果是特殊域的SPI设备,可以定义其时钟地址,例如这里的在 CPUS 域中的 SPI 控制器

sunxi_spi_t sunxi_spi0_lcd = {
.base = SUNXI_R_SPI_BASE,
.clk_reg = {
.ccu_base = SUNXI_R_PRCM_BASE,
.spi_clk_reg_offest = SUNXI_S_SPI_CLK_REG,
.spi_bgr_reg_offset = SUNXI_S_SPI_BGR_REG,
},
.id = 0,
.clk_rate = 75 * 1000 * 1000,
.gpio_cs = {GPIO_PIN(GPIO_PORTL, 10), GPIO_PERIPH_MUX6},
.gpio_sck = {GPIO_PIN(GPIO_PORTL, 11), GPIO_PERIPH_MUX6},
.gpio_mosi = {GPIO_PIN(GPIO_PORTL, 12), GPIO_PERIPH_MUX6},
};

使用 SPI 驱动 SPI LCD 屏

/* SPDX-License-Identifier: Apache-2.0 */

#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#include <types.h>

#include <config.h>
#include <log.h>
#include <timer.h>

#include <common.h>
#include <jmp.h>
#include <mmu.h>
#include <smalloc.h>
#include <sstdlib.h>
#include <string.h>

#include <cli.h>
#include <cli_shell.h>
#include <cli_termesc.h>

#include <reg-ncat.h>
#include <sys-clk.h>
#include <sys-dram.h>
#include <sys-i2c.h>
#include <sys-rtc.h>
#include <sys-sdcard.h>
#include <sys-sid.h>
#include <sys-spi.h>

#include <pmu/axp.h>

#include <fdt_wrapper.h>
#include <ff.h>
#include <sys-sdhci.h>
#include <uart.h>

#define SPI_LCD_COLOR_WHITE 0xFFFF
#define SPI_LCD_COLOR_BLACK 0x0000
#define SPI_LCD_COLOR_BLUE 0x001F
#define SPI_LCD_COLOR_BRED 0XF81F
#define SPI_LCD_COLOR_GRED 0XFFE0
#define SPI_LCD_COLOR_GBLUE 0X07FF
#define SPI_LCD_COLOR_RED 0xF800
#define SPI_LCD_COLOR_MAGENTA 0xF81F
#define SPI_LCD_COLOR_GREEN 0x07E0
#define SPI_LCD_COLOR_CYAN 0x7FFF
#define SPI_LCD_COLOR_YELLOW 0xFFE0

static sunxi_spi_t sunxi_spi0_lcd = {
.base = SUNXI_R_SPI_BASE,
.clk_reg = {
.ccu_base = SUNXI_R_PRCM_BASE,
.spi_clk_reg_offest = SUNXI_S_SPI_CLK_REG,
.spi_bgr_reg_offset = SUNXI_S_SPI_BGR_REG,
},
.id = 0,
.clk_rate = 75 * 1000 * 1000,
.gpio_cs = {GPIO_PIN(GPIO_PORTL, 10), GPIO_PERIPH_MUX6},
.gpio_sck = {GPIO_PIN(GPIO_PORTL, 11), GPIO_PERIPH_MUX6},
.gpio_mosi = {GPIO_PIN(GPIO_PORTL, 12), GPIO_PERIPH_MUX6},
};

static gpio_mux_t lcd_dc_pins = {
.pin = GPIO_PIN(GPIO_PORTL, 13),
.mux = GPIO_OUTPUT,
};

static gpio_mux_t lcd_res_pins = {
.pin = GPIO_PIN(GPIO_PORTL, 9),
.mux = GPIO_OUTPUT,
};

static gpio_mux_t lcd_blk_pins = {
.pin = GPIO_PIN(GPIO_PORTL, 8),
.mux = GPIO_OUTPUT,
};

static void LCD_Set_DC(uint8_t val) {
sunxi_gpio_set_value(lcd_dc_pins.pin, val);
}

static void LCD_Set_RES(uint8_t val) {
sunxi_gpio_set_value(lcd_res_pins.pin, val);
}

static void LCD_Write_Bus(uint8_t dat) {
uint8_t tx[1]; /* Transmit buffer */
tx[0] = dat;
/* Perform SPI transfer */
if (sunxi_spi_transfer(&sunxi_spi0_lcd, SPI_IO_SINGLE, tx, 1, 0, 0) < 0)
printk_error("SPI: SPI Xfer error!\n");
}

static void LCD_Write_Data_Bus(void *dat, uint32_t len) {
if (sunxi_spi_transfer(&sunxi_spi0_lcd, SPI_IO_SINGLE, dat, len, 0, 0) < 0)
printk_error("SPI: SPI Xfer error!\n");
}

static void LCD_WR_DATA(uint16_t dat) {
LCD_Write_Bus(dat >> 8);
LCD_Write_Bus(dat);
}

static void LCD_WR_DATA8(uint8_t dat) {
LCD_Write_Bus(dat);
}

static void LCD_WR_REG(uint8_t dat) {
LCD_Set_DC(0);
LCD_Write_Bus(dat);
LCD_Set_DC(1);
}

static void LCD_Address_Set(uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2) {
LCD_WR_REG(0x2a);
LCD_WR_DATA(x1 + 40);
LCD_WR_DATA(x2 + 40);
LCD_WR_REG(0x2b);
LCD_WR_DATA(y1 + 52);
LCD_WR_DATA(y2 + 52);
LCD_WR_REG(0x2c);
}

static void LCD_Open_BLK() {
sunxi_gpio_set_value(lcd_blk_pins.pin, 1);
}

#define LCD_W 240
#define LCD_H 135

static void LCD_Fill_All(uint16_t color) {
uint16_t i, j;
LCD_Address_Set(0, 0, LCD_W - 1, LCD_H - 1);
uint16_t *video_mem = smalloc(LCD_W * LCD_H);

for (uint32_t i = 0; i < LCD_W * LCD_H; i++) {
video_mem[i] = color;
}

LCD_Write_Data_Bus(video_mem, LCD_W * LCD_H * (sizeof(uint16_t) / sizeof(uint8_t)));

sfree(video_mem);
}

static void LCD_Init(void) {
sunxi_gpio_init(lcd_dc_pins.pin, lcd_dc_pins.mux);
sunxi_gpio_init(lcd_res_pins.pin, lcd_res_pins.mux);
sunxi_gpio_init(lcd_blk_pins.pin, lcd_blk_pins.mux);

dma_init();

if (sunxi_spi_init(&sunxi_spi0_lcd) != 0) {
printk_error("SPI: init failed\n");
}

LCD_Set_RES(0);
mdelay(100);
LCD_Set_RES(1);
mdelay(100);

LCD_WR_REG(0x11);
mdelay(120);
LCD_WR_REG(0x36);
LCD_WR_DATA8(0xA0);

LCD_WR_REG(0x3A);
LCD_WR_DATA8(0x05);

LCD_WR_REG(0xB2);
LCD_WR_DATA8(0x0C);
LCD_WR_DATA8(0x0C);
LCD_WR_DATA8(0x00);
LCD_WR_DATA8(0x33);
LCD_WR_DATA8(0x33);

LCD_WR_REG(0xB7);
LCD_WR_DATA8(0x35);

LCD_WR_REG(0xBB);
LCD_WR_DATA8(0x19);

LCD_WR_REG(0xC0);
LCD_WR_DATA8(0x2C);

LCD_WR_REG(0xC2);
LCD_WR_DATA8(0x01);

LCD_WR_REG(0xC3);
LCD_WR_DATA8(0x12);

LCD_WR_REG(0xC4);
LCD_WR_DATA8(0x20);

LCD_WR_REG(0xC6);
LCD_WR_DATA8(0x0F);

LCD_WR_REG(0xD0);
LCD_WR_DATA8(0xA4);
LCD_WR_DATA8(0xA1);

LCD_WR_REG(0xE0);
LCD_WR_DATA8(0xD0);
LCD_WR_DATA8(0x04);
LCD_WR_DATA8(0x0D);
LCD_WR_DATA8(0x11);
LCD_WR_DATA8(0x13);
LCD_WR_DATA8(0x2B);
LCD_WR_DATA8(0x3F);
LCD_WR_DATA8(0x54);
LCD_WR_DATA8(0x4C);
LCD_WR_DATA8(0x18);
LCD_WR_DATA8(0x0D);
LCD_WR_DATA8(0x0B);
LCD_WR_DATA8(0x1F);
LCD_WR_DATA8(0x23);

LCD_WR_REG(0xE1);
LCD_WR_DATA8(0xD0);
LCD_WR_DATA8(0x04);
LCD_WR_DATA8(0x0C);
LCD_WR_DATA8(0x11);
LCD_WR_DATA8(0x13);
LCD_WR_DATA8(0x2C);
LCD_WR_DATA8(0x3F);
LCD_WR_DATA8(0x44);
LCD_WR_DATA8(0x51);
LCD_WR_DATA8(0x2F);
LCD_WR_DATA8(0x1F);
LCD_WR_DATA8(0x1F);
LCD_WR_DATA8(0x20);
LCD_WR_DATA8(0x23);

LCD_WR_REG(0x21);

LCD_WR_REG(0x29);

LCD_Fill_All(0x0000);
}

#define SPLASH_START_X 52
#define SPLASH_START_Y 43
#define SPLASH_W 135
#define SPLASH_H 48

static void LCD_Show_Splash(uint8_t *splash_dest) {
uint16_t i, j, k = 0;
LCD_Address_Set(SPLASH_START_X, SPLASH_START_Y, SPLASH_START_X + SPLASH_W - 1, SPLASH_START_Y + SPLASH_H - 1);

uint16_t *video_mem = smalloc(SPLASH_W * SPLASH_H);

for (i = 0; i < SPLASH_W; i++) {
for (j = 0; j < SPLASH_H; j++) {
video_mem[k] = (splash_dest[k * 2] << 8) | splash_dest[k * 2 + 1];
k++;
}
}

LCD_Write_Data_Bus(video_mem, SPLASH_W * SPLASH_H * (sizeof(uint16_t) / sizeof(uint8_t)));

sfree(video_mem);
}

static const unsigned char ascii_1206[][12] = {
{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, /*" ",0*/
{0x00, 0x00, 0x04, 0x04, 0x04, 0x04, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00}, /*"!",1*/
{0x14, 0x14, 0x0A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, /*""",2*/
{0x00, 0x00, 0x0A, 0x0A, 0x1F, 0x0A, 0x0A, 0x1F, 0x0A, 0x0A, 0x00, 0x00}, /*"#",3*/
{0x00, 0x04, 0x0E, 0x15, 0x05, 0x06, 0x0C, 0x14, 0x15, 0x0E, 0x04, 0x00}, /*"$",4*/
{0x00, 0x00, 0x12, 0x15, 0x0D, 0x15, 0x2E, 0x2C, 0x2A, 0x12, 0x00, 0x00}, /*"%",5*/
{0x00, 0x00, 0x04, 0x0A, 0x0A, 0x36, 0x15, 0x15, 0x29, 0x16, 0x00, 0x00}, /*"&",6*/
{0x02, 0x02, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, /*"'",7*/
{0x10, 0x08, 0x08, 0x04, 0x04, 0x04, 0x04, 0x04, 0x08, 0x08, 0x10, 0x00}, /*"(",8*/
{0x02, 0x04, 0x04, 0x08, 0x08, 0x08, 0x08, 0x08, 0x04, 0x04, 0x02, 0x00}, /*")",9*/
{0x00, 0x00, 0x00, 0x04, 0x15, 0x0E, 0x0E, 0x15, 0x04, 0x00, 0x00, 0x00}, /*"*",10*/
{0x00, 0x00, 0x00, 0x08, 0x08, 0x3E, 0x08, 0x08, 0x00, 0x00, 0x00, 0x00}, /*"+",11*/
{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x01, 0x00}, /*",",12*/
{0x00, 0x00, 0x00, 0x00, 0x00, 0x3F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, /*"-",13*/
{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00}, /*".",14*/
{0x00, 0x20, 0x10, 0x10, 0x08, 0x08, 0x04, 0x04, 0x02, 0x02, 0x01, 0x00}, /*"/",15*/
{0x00, 0x00, 0x0E, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x0E, 0x00, 0x00}, /*"0",16*/
{0x00, 0x00, 0x04, 0x06, 0x04, 0x04, 0x04, 0x04, 0x04, 0x0E, 0x00, 0x00}, /*"1",17*/
{0x00, 0x00, 0x0E, 0x11, 0x11, 0x08, 0x04, 0x02, 0x01, 0x1F, 0x00, 0x00}, /*"2",18*/
{0x00, 0x00, 0x0E, 0x11, 0x10, 0x0C, 0x10, 0x10, 0x11, 0x0E, 0x00, 0x00}, /*"3",19*/
{0x00, 0x00, 0x08, 0x0C, 0x0C, 0x0A, 0x09, 0x1F, 0x08, 0x1C, 0x00, 0x00}, /*"4",20*/
{0x00, 0x00, 0x1F, 0x01, 0x01, 0x0F, 0x11, 0x10, 0x11, 0x0E, 0x00, 0x00}, /*"5",21*/
{0x00, 0x00, 0x0C, 0x12, 0x01, 0x0D, 0x13, 0x11, 0x11, 0x0E, 0x00, 0x00}, /*"6",22*/
{0x00, 0x00, 0x1E, 0x10, 0x08, 0x08, 0x04, 0x04, 0x04, 0x04, 0x00, 0x00}, /*"7",23*/
{0x00, 0x00, 0x0E, 0x11, 0x11, 0x0E, 0x11, 0x11, 0x11, 0x0E, 0x00, 0x00}, /*"8",24*/
{0x00, 0x00, 0x0E, 0x11, 0x11, 0x19, 0x16, 0x10, 0x09, 0x06, 0x00, 0x00}, /*"9",25*/
{0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00}, /*":",26*/
{0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x04, 0x04, 0x00}, /*";",27*/
{0x00, 0x00, 0x10, 0x08, 0x04, 0x02, 0x02, 0x04, 0x08, 0x10, 0x00, 0x00}, /*"<",28*/
{0x00, 0x00, 0x00, 0x00, 0x3F, 0x00, 0x3F, 0x00, 0x00, 0x00, 0x00, 0x00}, /*"=",29*/
{0x00, 0x00, 0x02, 0x04, 0x08, 0x10, 0x10, 0x08, 0x04, 0x02, 0x00, 0x00}, /*">",30*/
{0x00, 0x00, 0x0E, 0x11, 0x11, 0x08, 0x04, 0x04, 0x00, 0x04, 0x00, 0x00}, /*"?",31*/
{0x00, 0x00, 0x1C, 0x22, 0x29, 0x2D, 0x2D, 0x1D, 0x22, 0x1C, 0x00, 0x00}, /*"@",32*/
{0x00, 0x00, 0x04, 0x04, 0x0C, 0x0A, 0x0A, 0x1E, 0x12, 0x33, 0x00, 0x00}, /*"A",33*/
{0x00, 0x00, 0x0F, 0x12, 0x12, 0x0E, 0x12, 0x12, 0x12, 0x0F, 0x00, 0x00}, /*"B",34*/
{0x00, 0x00, 0x1E, 0x11, 0x01, 0x01, 0x01, 0x01, 0x11, 0x0E, 0x00, 0x00}, /*"C",35*/
{0x00, 0x00, 0x0F, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x0F, 0x00, 0x00}, /*"D",36*/
{0x00, 0x00, 0x1F, 0x12, 0x0A, 0x0E, 0x0A, 0x02, 0x12, 0x1F, 0x00, 0x00}, /*"E",37*/
{0x00, 0x00, 0x1F, 0x12, 0x0A, 0x0E, 0x0A, 0x02, 0x02, 0x07, 0x00, 0x00}, /*"F",38*/
{0x00, 0x00, 0x1C, 0x12, 0x01, 0x01, 0x39, 0x11, 0x12, 0x0C, 0x00, 0x00}, /*"G",39*/
{0x00, 0x00, 0x33, 0x12, 0x12, 0x1E, 0x12, 0x12, 0x12, 0x33, 0x00, 0x00}, /*"H",40*/
{0x00, 0x00, 0x1F, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x1F, 0x00, 0x00}, /*"I",41*/
{0x00, 0x00, 0x3E, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x09, 0x07}, /*"J",42*/
{0x00, 0x00, 0x37, 0x12, 0x0A, 0x06, 0x0A, 0x12, 0x12, 0x37, 0x00, 0x00}, /*"K",43*/
{0x00, 0x00, 0x07, 0x02, 0x02, 0x02, 0x02, 0x02, 0x22, 0x3F, 0x00, 0x00}, /*"L",44*/
{0x00, 0x00, 0x3B, 0x1B, 0x1B, 0x1B, 0x15, 0x15, 0x15, 0x35, 0x00, 0x00}, /*"M",45*/
{0x00, 0x00, 0x3B, 0x12, 0x16, 0x16, 0x1A, 0x1A, 0x12, 0x17, 0x00, 0x00}, /*"N",46*/
{0x00, 0x00, 0x0E, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x0E, 0x00, 0x00}, /*"O",47*/
{0x00, 0x00, 0x0F, 0x12, 0x12, 0x0E, 0x02, 0x02, 0x02, 0x07, 0x00, 0x00}, /*"P",48*/
{0x00, 0x00, 0x0E, 0x11, 0x11, 0x11, 0x11, 0x17, 0x19, 0x0E, 0x18, 0x00}, /*"Q",49*/
{0x00, 0x00, 0x0F, 0x12, 0x12, 0x0E, 0x0A, 0x12, 0x12, 0x37, 0x00, 0x00}, /*"R",50*/
{0x00, 0x00, 0x1E, 0x11, 0x01, 0x06, 0x08, 0x10, 0x11, 0x0F, 0x00, 0x00}, /*"S",51*/
{0x00, 0x00, 0x1F, 0x15, 0x04, 0x04, 0x04, 0x04, 0x04, 0x0E, 0x00, 0x00}, /*"T",52*/
{0x00, 0x00, 0x33, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x0C, 0x00, 0x00}, /*"U",53*/
{0x00, 0x00, 0x33, 0x12, 0x12, 0x0A, 0x0A, 0x0C, 0x04, 0x04, 0x00, 0x00}, /*"V",54*/
{0x00, 0x00, 0x15, 0x15, 0x15, 0x15, 0x0E, 0x0A, 0x0A, 0x0A, 0x00, 0x00}, /*"W",55*/
{0x00, 0x00, 0x1B, 0x0A, 0x0A, 0x04, 0x04, 0x0A, 0x0A, 0x1B, 0x00, 0x00}, /*"X",56*/
{0x00, 0x00, 0x1B, 0x0A, 0x0A, 0x0A, 0x04, 0x04, 0x04, 0x0E, 0x00, 0x00}, /*"Y",57*/
{0x00, 0x00, 0x1F, 0x09, 0x08, 0x04, 0x04, 0x02, 0x12, 0x1F, 0x00, 0x00}, /*"Z",58*/
{0x1C, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x1C, 0x00}, /*"[",59*/
{0x00, 0x02, 0x02, 0x04, 0x04, 0x04, 0x08, 0x08, 0x08, 0x10, 0x10, 0x00}, /*"\",60*/
{0x0E, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x0E, 0x00}, /*"]",61*/
{0x04, 0x0A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, /*"^",62*/
{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3F}, /*"_",63*/
{0x02, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, /*"`",64*/
{0x00, 0x00, 0x00, 0x00, 0x00, 0x0C, 0x12, 0x1C, 0x12, 0x3C, 0x00, 0x00}, /*"a",65*/
{0x00, 0x03, 0x02, 0x02, 0x02, 0x0E, 0x12, 0x12, 0x12, 0x0E, 0x00, 0x00}, /*"b",66*/
{0x00, 0x00, 0x00, 0x00, 0x00, 0x1C, 0x12, 0x02, 0x12, 0x0C, 0x00, 0x00}, /*"c",67*/
{0x00, 0x18, 0x10, 0x10, 0x10, 0x1C, 0x12, 0x12, 0x12, 0x3C, 0x00, 0x00}, /*"d",68*/
{0x00, 0x00, 0x00, 0x00, 0x00, 0x0C, 0x12, 0x1E, 0x02, 0x1C, 0x00, 0x00}, /*"e",69*/
{0x00, 0x18, 0x24, 0x04, 0x04, 0x1E, 0x04, 0x04, 0x04, 0x1E, 0x00, 0x00}, /*"f",70*/
{0x00, 0x00, 0x00, 0x00, 0x00, 0x3C, 0x12, 0x0C, 0x02, 0x1C, 0x22, 0x1C}, /*"g",71*/
{0x00, 0x03, 0x02, 0x02, 0x02, 0x0E, 0x12, 0x12, 0x12, 0x37, 0x00, 0x00}, /*"h",72*/
{0x00, 0x04, 0x04, 0x00, 0x00, 0x06, 0x04, 0x04, 0x04, 0x0E, 0x00, 0x00}, /*"i",73*/
{0x00, 0x08, 0x08, 0x00, 0x00, 0x0C, 0x08, 0x08, 0x08, 0x08, 0x08, 0x07}, /*"j",74*/
{0x00, 0x03, 0x02, 0x02, 0x02, 0x1A, 0x0A, 0x06, 0x0A, 0x13, 0x00, 0x00}, /*"k",75*/
{0x00, 0x07, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x1F, 0x00, 0x00}, /*"l",76*/
{0x00, 0x00, 0x00, 0x00, 0x00, 0x0F, 0x15, 0x15, 0x15, 0x15, 0x00, 0x00}, /*"m",77*/
{0x00, 0x00, 0x00, 0x00, 0x00, 0x0F, 0x12, 0x12, 0x12, 0x37, 0x00, 0x00}, /*"n",78*/
{0x00, 0x00, 0x00, 0x00, 0x00, 0x0C, 0x12, 0x12, 0x12, 0x0C, 0x00, 0x00}, /*"o",79*/
{0x00, 0x00, 0x00, 0x00, 0x00, 0x0F, 0x12, 0x12, 0x12, 0x0E, 0x02, 0x07}, /*"p",80*/
{0x00, 0x00, 0x00, 0x00, 0x00, 0x1C, 0x12, 0x12, 0x12, 0x1C, 0x10, 0x38}, /*"q",81*/
{0x00, 0x00, 0x00, 0x00, 0x00, 0x1B, 0x06, 0x02, 0x02, 0x07, 0x00, 0x00}, /*"r",82*/
{0x00, 0x00, 0x00, 0x00, 0x00, 0x1E, 0x02, 0x0C, 0x10, 0x1E, 0x00, 0x00}, /*"s",83*/
{0x00, 0x00, 0x00, 0x04, 0x04, 0x1E, 0x04, 0x04, 0x04, 0x1C, 0x00, 0x00}, /*"t",84*/
{0x00, 0x00, 0x00, 0x00, 0x00, 0x1B, 0x12, 0x12, 0x12, 0x3C, 0x00, 0x00}, /*"u",85*/
{0x00, 0x00, 0x00, 0x00, 0x00, 0x1B, 0x0A, 0x0A, 0x04, 0x04, 0x00, 0x00}, /*"v",86*/
{0x00, 0x00, 0x00, 0x00, 0x00, 0x15, 0x15, 0x0E, 0x0A, 0x0A, 0x00, 0x00}, /*"w",87*/
{0x00, 0x00, 0x00, 0x00, 0x00, 0x1B, 0x0A, 0x04, 0x0A, 0x1B, 0x00, 0x00}, /*"x",88*/
{0x00, 0x00, 0x00, 0x00, 0x00, 0x33, 0x12, 0x12, 0x0C, 0x08, 0x04, 0x03}, /*"y",89*/
{0x00, 0x00, 0x00, 0x00, 0x00, 0x1E, 0x08, 0x04, 0x04, 0x1E, 0x00, 0x00}, /*"z",90*/
{0x18, 0x08, 0x08, 0x08, 0x08, 0x0C, 0x08, 0x08, 0x08, 0x08, 0x18, 0x00}, /*"{",91*/
{0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08}, /*"|",92*/
{0x06, 0x04, 0x04, 0x04, 0x04, 0x08, 0x04, 0x04, 0x04, 0x04, 0x06, 0x00}, /*"}",93*/
{0x16, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, /*"~",94*/
};

static void LCD_ShowChar(uint16_t x, uint16_t y, uint8_t num, uint16_t fc, uint16_t bc, uint8_t sizey) {
uint8_t temp, sizex, t, m = 0;
uint16_t i, TypefaceNum;// Number of bytes for one character
uint16_t x0 = x;
sizex = sizey / 2;
TypefaceNum = (sizex / 8 + ((sizex % 8) ? 1 : 0)) * sizey;
num = num - ' '; // Get the offset value
LCD_Address_Set(x, y, x + sizex - 1, y + sizey - 1);// Set the cursor position
for (i = 0; i < TypefaceNum; i++) {
temp = ascii_1206[num][i];// Call 6x12 font
for (t = 0; t < 8; t++) {
if (temp & (0x01 << t))
LCD_WR_DATA(fc);
else
LCD_WR_DATA(bc);
m++;
if (m % sizex == 0) {
m = 0;
break;
}
}
}
}


static void LCD_ShowString(uint16_t x, uint16_t y, const uint8_t *p, uint16_t fc, uint16_t bc, uint8_t sizey) {
printk_debug("LCD: Show String: \"%s\"\n", p);
while (*p != '\0') {
LCD_ShowChar(x, y, *p, fc, bc, sizey);
x += sizey / 2;
p++;
}
}

int main() {
LCD_Init();
LCD_ShowString(0, 0, "Hello World!", SPI_LCD_COLOR_GREEN, SPI_LCD_COLOR_BLACK, 12);
}