spi系列文章:
1.spi 硬件、协议
2.spi(GPIO模拟)
3.spi驱动
设置时钟
spi在arm-cortex系列当中一般与ssp联系一起。ssp兼容spi,两者区别也不大。在4418上面没有用spi模式调试成功,用spp却成功了,参考了内核初始化源码,也是ssp模式,没办法先用ssp模式。
spi控制器的初始化首先需要配置好时钟,在4418的spi/ssp控制器中时钟由两个来源。一个是pclk,一个是sspclk。pclk是cpu访问spi寄存器的时钟,也就是说cpu改变spi/ssp寄存器的时钟,一般等于cpu主频,这个一般不需要管,在4418上也无法修改。sspclk决定了spi/ssp控制器的内部速度。最后分频输出为sspclkout,这个才是spi/ssp的时钟。附上框图如下。
spi/ssp时钟线:
所以需要设置sspclk分频、一级分频、二级分频。
sspclk由时钟发生器产生,设置一下时钟发生器。频率我设置得比较高。
ssp_clk = (struct SSP_CLK *)ioremap(0xc00ac000,sizeof(struct SSP_CLK)); //映射地址
ssp_clk->clk = 0;
ssp_clk->clk &= ~(1 << 1);
ssp_clk->clk |= (2 << 2);
ssp_clk->clk |= (2 << 5);
ssp_clk->clk &= ~(1 << 15);
ssp_clk->enb &= ~(1 << 2);
udelay(100);
ssp_clk->enb |= (1 << 2);
spi/ssp的一级分频和二级分频由后面的配置寄存器控制。
spi/ssp控制器复位
在设置spi/ssp控制器之前先做一下复位。复位的作用是在spi/ssp死锁的时候用来恢复它们的功能的,初始化的时候也要做一下。根据手册描述需要复位两个寄存器,时间不能少于一个pclk。
reset = (unsigned int *)ioremap(0xc0012004,sizeof(int)); //映射地址
*reset |= (1 << 11);
*reset |= (1 << 12);
*reset &= ~(1 << 11);
*reset &= ~(1 << 12);
udelay(100);
*reset |= (1 << 11);
*reset |= (1 << 12);
然后设置GPIO功能
这边spi功能为1。
nxp_soc_gpio_set_io_func(PAD_GPIO_C + 29, 1); // 设置gpio功能
nxp_soc_gpio_set_io_func(PAD_GPIO_C + 30, 1); // 设置gpio功能
nxp_soc_gpio_set_io_func(PAD_GPIO_C + 31, 1); // 设置gpio功能
nxp_soc_gpio_set_io_func(PAD_GPIO_D + 0, 1); // 设置gpio功能
初始化spi/ssp寄存器
spi/ssp需要设置的寄存器主要有三个sspcr0、sspcr1 、sspdmacr 。设置了spi的工作模式、频率、dma。其中sspcr0设置一级分频的值,范围是0~ 255最后需要加一作为分频系数,sspcr1 设置了二级分频的值范围是2~254。
频率公式:
内容比较简单,就直接上代码了。
spi0 = (struct SPI *)ioremap(0xc005b000,sizeof(struct SPI)); //映射地址
spi0->sspcr0 = 0;
spi0->sspcr0 |= (7 << 0); //八位模式
spi0->sspcr0 &= ~(1 << 4); //spi模式
spi0->sspcr0 &= ~(1 << 6); //下降沿发送
spi0->sspcr0 &= ~(1 << 7); //空闲低电平
spi0->sspcr0 |= (0x00 << 8); //CLK Rate 0~255
spi0->sspcr1 = 0;
spi0->sspcr1 &= ~(1 << 0); //普通传输模式
spi0->sspcr1 |= (1 << 1); //同步传输模式
spi0->sspcr1 &= ~(1 << 2); //主机模式
spi0->sspcr1 |= (1 << 3); //从机模式不产生时钟
spi0->sspcpsr |= 0x04; //CLK Rate 2~254
spi0->sspimsc = 0; //disable interrupt
spi0->sspicr = 3; //cls interrupt
spi0->sspdmacr = 0; //disable dma
整体代码
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/vmalloc.h>
#include <asm/io.h>
#include <linux/gpio.h>
#include <mach/soc.h>
#include <linux/delay.h>
#include <mach/platform.h>
#include <linux/fs.h>
#include <linux/device.h>
#include <linux/cdev.h>
#include <linux/slab.h>
#include <asm/uaccess.h>
#include <linux/irq.h>
#include <linux/interrupt.h>
MODULE_LICENSE("GPL");
dev_t devid;
struct cdev char_dev;
struct class * char_class;
int buffer_size = 500000;
char * char_data;
const unsigned int WAIT_TIME = 20000;
#pragma pack(4)
static struct SPI{
volatile unsigned int sspcr0;
volatile unsigned int sspcr1;
volatile unsigned int sspdr;
volatile unsigned int sspsr;
volatile unsigned int sspcpsr;
volatile unsigned int sspimsc;
volatile unsigned int sspris;
volatile unsigned int sspmis;
volatile unsigned int sspicr;
volatile unsigned int sspdmacr;
}* spi0;
static struct SSP_CLK{
unsigned int enb;
unsigned int clk;
}* ssp_clk;
#pragma pack()
unsigned int * reset;
inline int spi_busy(void){
return (spi0->sspsr & (1 << 4));
// return (spi0->sspsr & (1 << 4)) >> 4;
}
void print_state(void){
if(spi0->sspsr & (1 << 0)) printk("Transmit FIFO is empty.\n");
if(spi0->sspsr & (1 << 1)) printk("Transmit FIFO is not full.\n");
if(spi0->sspsr & (1 << 2)) printk("Receive FIFO is not empty.\n");
if(spi0->sspsr & (1 << 3)) printk("Receive FIFO is full.\n");
if(spi0->sspsr & (1 << 4)) printk("SSP is currently transmitting and/or receiving a frame or the transmit FIFO is not empty.\n");
}
inline void write_byte(unsigned char spi_Byte)
{
volatile int wait_time = 0;
while(spi_busy() != 0){
wait_time++;
if(wait_time > WAIT_TIME){
printk("out time\n");
return;
}
}
spi0->sspdr = spi_Byte;
}
static ssize_t write(struct file * fl, const char __user * buf, size_t len, loff_t * offset){
int ret = 0, copy_len, i, data_len = buffer_size, _offset, let;
if(fl->f_pos + len > data_len)
copy_len = data_len - fl->f_pos; //超过长度,复制剩余部分
else
copy_len = len; //没超过
ret = copy_len;
_offset = 0;
while(ret){
let = copy_from_user(char_data + fl->f_pos + _offset,buf + _offset,ret);
_offset = _offset + ret - let;
ret = let;
}
ret = copy_len - ret;
for (i = 0; i < ret; ++i)
{
write_byte(* (char_data + fl->f_pos + i));
}
// *offset += ret; //移动文件指针
return ret;
}
static long ioctl(struct file * fl, unsigned int cmd, unsigned long arg){
unsigned int dir,size;
dir = _IOC_DIR(cmd);
size = _IOC_SIZE(cmd);
if(dir == _IOC_WRITE){
}
return 0;
}
struct file_operations my_opts = {
.owner = THIS_MODULE,
.unlocked_ioctl = ioctl,
.write = write
};
static int init_dev(void){
int ret = 0;
devid = MKDEV(241, 1); //换算设备号
ret = register_chrdev_region(devid, 1, "char_test");//注册设备,在/proc/drivers下面可以看到
if (ret < 0)
goto err0;
cdev_init(&char_dev,&my_opts); //绑定opt结构体
char_dev.owner = THIS_MODULE;
ret = cdev_add(&char_dev,devid,1); //注册字符设备驱动
if (ret < 0)
goto err1;
char_class = class_create(THIS_MODULE,"char_test"); //在/sys/class中创建文件夹
device_create(char_class,NULL,devid,NULL,"char_test_dev_%d",1);//在上一步文件夹中创建char_test_dev_1
char_data = vzalloc(buffer_size);
err1:
unregister_chrdev_region(devid, 1);
err0:
return ret;
}
static int init_gpio(void){
nxp_soc_gpio_set_io_func(PAD_GPIO_C + 29, 1); // 设置gpio功能
nxp_soc_gpio_set_io_func(PAD_GPIO_C + 30, 1); // 设置gpio功能
nxp_soc_gpio_set_io_func(PAD_GPIO_C + 31, 1); // 设置gpio功能
nxp_soc_gpio_set_io_func(PAD_GPIO_D + 0, 1); // 设置gpio功能
return 0;
}
static int un_init_gpio(void){
nxp_soc_gpio_set_io_func(PAD_GPIO_C + 29, 0); // 设置gpio功能
nxp_soc_gpio_set_io_func(PAD_GPIO_C + 30, 0); // 设置gpio功能
nxp_soc_gpio_set_io_func(PAD_GPIO_C + 31, 0); // 设置gpio功能
nxp_soc_gpio_set_io_func(PAD_GPIO_D + 0, 0); // 设置gpio功能
return 0;
}
static int sys_reset(void){
*reset |= (1 << 11);
*reset |= (1 << 12);
*reset &= ~(1 << 11);
*reset &= ~(1 << 12);
udelay(100);
*reset |= (1 << 11);
*reset |= (1 << 12);
ssp_clk->clk = 0;
ssp_clk->clk &= ~(1 << 1);
ssp_clk->clk |= (2 << 2);
ssp_clk->clk |= (2 << 5);
ssp_clk->clk &= ~(1 << 15);
ssp_clk->enb &= ~(1 << 2);
udelay(100);
ssp_clk->enb |= (1 << 2);
return 0;
}
static int __init spi_init(void){
int ret = 0;
spi0 = (struct SPI *)ioremap(0xc005b000,sizeof(struct SPI)); //映射地址
reset = (unsigned int *)ioremap(0xc0012004,sizeof(int)); //映射地址
ssp_clk = (struct SSP_CLK *)ioremap(0xc00ac000,sizeof(struct SSP_CLK)); //映射地址
ret = init_dev();
if (ret < 0) return ret;
ret = sys_reset();
if (ret < 0) return ret;
ret = init_gpio();
if (ret < 0) return ret;
spi0->sspcr0 = 0;
spi0->sspcr0 |= (7 << 0); //八位模式
spi0->sspcr0 &= ~(1 << 4); //spi模式
spi0->sspcr0 &= ~(1 << 6); //下降沿发送
spi0->sspcr0 &= ~(1 << 7); //空闲低电平
spi0->sspcr0 |= (0x00 << 8); //CLK Rate 0~255
spi0->sspcr1 = 0;
spi0->sspcr1 &= ~(1 << 0); //普通传输模式
spi0->sspcr1 |= (1 << 1); //同步传输模式
spi0->sspcr1 &= ~(1 << 2); //主机模式
spi0->sspcr1 |= (1 << 3); //从机模式不产生时钟
spi0->sspcpsr |= 0x04; //CLK Rate 2~254
spi0->sspimsc = 0; //disable interrupt
spi0->sspicr = 3; //cls interrupt
spi0->sspdmacr = 0; //disable dma
printk("spi init\n");
return 0;
}
static void __exit spi_exit(void){
iounmap(spi0);
iounmap(reset);
un_init_gpio();
unregister_chrdev_region(devid, 1);
cdev_del(&char_dev);
device_destroy(char_class,devid);
class_destroy(char_class);
printk("spi exit\n");
}
module_init(spi_init);
module_exit(spi_exit);
application
#include <stdio.h>
#include <linux/ioctl.h>
#include <sys/ioctl.h>
#include <unistd.h>
#include <fcntl.h>
#include <unistd.h>
#include "main.h"
int fd_spi; //spi
int fd_cs; //cd
int fd_rs; //数据/指令切换
int fd_re; //复位
_lcd_dev lcddev;
void LCD_WR_REG(unsigned char Reg)
{
char data[] = {
Reg
};
write(fd_rs,"0",1);
write(fd_cs,"0",1);
write(fd_spi,data,1);
write(fd_cs,"1",1);
}
void LCD_WR_DATA(unsigned char Data)
{
char data[] = {
Data
};
write(fd_rs,"1",1);
write(fd_cs,"0",1);
write(fd_spi,data,1);
write(fd_cs,"1",1);
}
void LCD_WRITE_DATA(unsigned char * data,int count)
{
write(fd_rs,"1",1);
write(fd_cs,"0",1);
write(fd_spi,data,count);
write(fd_cs,"1",1);
}
void LCD_WR_DATA_16Bit(unsigned short Data)
{
//18Bit
LCD_WR_DATA((Data>>8)&0xF8);//RED
LCD_WR_DATA((Data>>3)&0xFC);//GREEN
LCD_WR_DATA(Data<<3);//BLUE
}
void LCD_WriteReg(unsigned char LCD_Reg, unsigned char LCD_RegValue)
{
LCD_WR_REG(LCD_Reg);
LCD_WR_DATA(LCD_RegValue);
}
void LCD_WriteRAM_Prepare(void)
{
LCD_WR_REG(lcddev.wramcmd);
}
void LCD_SetWindows(unsigned short xStar, unsigned short yStar,unsigned short xEnd,unsigned short yEnd)
{
LCD_WR_REG(lcddev.setxcmd);
LCD_WR_DATA(xStar>>8);
LCD_WR_DATA(0x00FF&xStar);
LCD_WR_DATA(xEnd>>8);
LCD_WR_DATA(0x00FF&xEnd);
LCD_WR_REG(lcddev.setycmd);
LCD_WR_DATA(yStar>>8);
LCD_WR_DATA(0x00FF&yStar);
LCD_WR_DATA(yEnd>>8);
LCD_WR_DATA(0x00FF&yEnd);
LCD_WriteRAM_Prepare(); //开始写入GRAM
}
void LCD_Clear(unsigned short Color)
{
unsigned int i,j;
unsigned char data[lcddev.width * lcddev.height * 3];
LCD_SetWindows(0,0,lcddev.width-1,lcddev.height-1);
for(i=0;i<lcddev.width * lcddev.height;i++)
{
data[i * 3] = (Color>>8)&0xF8;
data[i * 3 + 1] = (Color>>3)&0xFC;
data[i * 3 + 2] = (Color<<3);
}
LCD_WRITE_DATA(data, lcddev.width * lcddev.height * 3);
}
void LCD_DrawPoint(unsigned short x,unsigned short y)
{
LCD_SetWindows(x,y,x,y);//设置光标位置
LCD_WR_DATA(POINT_COLOR);
}
void LCD_SetCursor(unsigned short Xpos, unsigned short Ypos)
{
LCD_SetWindows(Xpos,Ypos,Xpos,Ypos);
}
void LCD_direction(unsigned char direction)
{
lcddev.setxcmd=0x2A;
lcddev.setycmd=0x2B;
lcddev.wramcmd=0x2C;
switch(direction){
case 0:
lcddev.width=LCD_W;
lcddev.height=LCD_H;
LCD_WriteReg(0x36,(1<<3)|(0<<6)|(0<<7));//BGR==1,MY==0,MX==0,MV==0
break;
case 1:
lcddev.width=LCD_H;
lcddev.height=LCD_W;
LCD_WriteReg(0x36,(1<<3)|(0<<7)|(1<<6)|(1<<5));//BGR==1,MY==1,MX==0,MV==1
break;
case 2:
lcddev.width=LCD_W;
lcddev.height=LCD_H;
LCD_WriteReg(0x36,(1<<3)|(1<<6)|(1<<7));//BGR==1,MY==0,MX==0,MV==0
break;
case 3:
lcddev.width=LCD_H;
lcddev.height=LCD_W;
LCD_WriteReg(0x36,(1<<3)|(1<<7)|(1<<5));//BGR==1,MY==1,MX==0,MV==1
break;
default:break;
}
}
void LCD_Reset(void)
{
write(fd_re,"1",1);
sleep(0.2);
write(fd_re,"0",1);
sleep(0.3);
write(fd_re,"1",1);
sleep(0.2);
}
void LCD_Init(void)
{
LCD_Reset(); //初始化之前复位
//************* ILI9488初始化**********//
LCD_WR_REG(0XF7);
LCD_WR_DATA(0xA9);
LCD_WR_DATA(0x51);
LCD_WR_DATA(0x2C);
LCD_WR_DATA(0x82);
LCD_WR_REG(0xC0);
LCD_WR_DATA(0x11);
LCD_WR_DATA(0x09);
LCD_WR_REG(0xC1);
LCD_WR_DATA(0x41);
LCD_WR_REG(0XC5);
LCD_WR_DATA(0x00);
LCD_WR_DATA(0x0A);
LCD_WR_DATA(0x80);
LCD_WR_REG(0xB1);
LCD_WR_DATA(0xB0);
LCD_WR_DATA(0x11);
LCD_WR_REG(0xB4);
LCD_WR_DATA(0x02);
LCD_WR_REG(0xB6);
LCD_WR_DATA(0x02);
LCD_WR_DATA(0x42);
LCD_WR_REG(0xB7);
LCD_WR_DATA(0xc6);
LCD_WR_REG(0xBE);
LCD_WR_DATA(0x00);
LCD_WR_DATA(0x04);
LCD_WR_REG(0xE9);
LCD_WR_DATA(0x00);
LCD_WR_REG(0x36);
LCD_WR_DATA((1<<3)|(0<<7)|(1<<6)|(1<<5));
LCD_WR_REG(0x3A);
LCD_WR_DATA(0x66);
LCD_WR_REG(0xE0);
LCD_WR_DATA(0x00);
LCD_WR_DATA(0x07);
LCD_WR_DATA(0x10);
LCD_WR_DATA(0x09);
LCD_WR_DATA(0x17);
LCD_WR_DATA(0x0B);
LCD_WR_DATA(0x41);
LCD_WR_DATA(0x89);
LCD_WR_DATA(0x4B);
LCD_WR_DATA(0x0A);
LCD_WR_DATA(0x0C);
LCD_WR_DATA(0x0E);
LCD_WR_DATA(0x18);
LCD_WR_DATA(0x1B);
LCD_WR_DATA(0x0F);
LCD_WR_REG(0XE1);
LCD_WR_DATA(0x00);
LCD_WR_DATA(0x17);
LCD_WR_DATA(0x1A);
LCD_WR_DATA(0x04);
LCD_WR_DATA(0x0E);
LCD_WR_DATA(0x06);
LCD_WR_DATA(0x2F);
LCD_WR_DATA(0x45);
LCD_WR_DATA(0x43);
LCD_WR_DATA(0x02);
LCD_WR_DATA(0x0A);
LCD_WR_DATA(0x09);
LCD_WR_DATA(0x32);
LCD_WR_DATA(0x36);
LCD_WR_DATA(0x0F);
LCD_WR_REG(0x11);
sleep(0.2);
LCD_WR_REG(0x29);
//设置LCD属性参数
LCD_direction(USE_HORIZONTAL);//设置LCD显示方向
}
int export_gpio(int no){
char cmd[100];
int ret = 0;
sprintf(cmd,"echo %d > /sys/class/gpio/export",no);
ret = system(cmd);
if(ret != 0) return ret;
sprintf(cmd,"echo out > /sys/class/gpio/gpio%d/direction",no);
ret = system(cmd);
return ret;
}
int init_cs(void)
{
return export_gpio(68);
}
int init_rs(void)
{
return export_gpio(71);
}
int init_re(void)
{
return export_gpio(72);
}
int main(){
int ret,cmd,i;
if(access(CS_GPIO,F_OK) == -1){
ret = init_cs();
}
if(ret != 0){
perror("export gpio 68 error");
return ret;
}
if(access(RS_GPIO,F_OK) == -1){
ret = init_rs();
}
if(ret != 0){
perror("export gpio 71 error");
return ret;
}
if(access(RE_GPIO,F_OK) == -1){
ret = init_re();
}
if(ret != 0){
perror("export gpio 72 error");
return ret;
}
fd_rs = open(RS_GPIO,O_RDWR);
ret = fd_rs;
if(ret < 0){
perror("open");
return ret;
}
fd_cs = open(CS_GPIO,O_RDWR);
ret = fd_cs;
if(ret < 0){
perror("open");
return ret;
}
fd_re = open(RE_GPIO,O_RDWR);
ret = fd_re;
if(ret < 0){
perror("open");
return ret;
}
fd_spi = open("/dev/char_test_dev_1",O_RDWR);
ret = fd_spi;
if(ret < 0){
perror("open /dev/char_test_dev_1 error");
return ret;
}
LCD_Init();
for (i = 0; i < 1; ++i)
{
LCD_Clear(RED);
LCD_Clear(GREEN);
LCD_Clear(BLUE);
LCD_Clear(RED);
}
close(fd_rs);
close(fd_cs);
close(fd_spi);
return 0;
}