In the past two days, I referred to the information on the Internet and wrote an SPI driver myself, which passed the actual test.
Hardware platform: mini2440 uses SPI1 of S3C2440 (there are 2 SPI modules in total)
Operating system: linux-2.6.32.2
Test method: Short-circuit the MISO and MOSI pins of the SPI, so that when reading data, the first dummy byte sent is the received byte.
Below is the source code of the driver (mini2440_spi.c):
/************************************************ **/
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
int loopChar=0x88;
module_param(loopChar,int,S_IRUGO);
static int spi_major = 55;
#define spi_name "mini2440_spi"
struct cdev spiCdev;
/************************************************ ****/
static int spi_open(struct inode *,struct file *);
static int spi_release(struct inode *,struct file *);
static ssize_t spi_write(struct file *filp,const char *buf,size_t count,loff_t *f_ops);
static ssize_t spi_read(struct file *filp,char *buf,size_t count,loff_t *f_ops);
static ssize_t spi_ioctl(struct inode *inode,struct file *filp,unsigned int cmd,unsigned long data);
volatile int *spi_gpfcon=NULL;//GPF Part define
volatile int *spi_gpfdat=NULL;
volatile int *spi_gpfup=NULL;
volatile int *spi_gpgcon=NULL;//GPG Part define
volatile int *spi_gpgdat=NULL;
volatile int *spi_gpgup=NULL;
volatile int *s3c2440_clkcon;
volatile int *spi_spcon1;//SPI Part define
volatile int *spi_spsta1;
volatile int *spi_sppin1;
volatile int *spi_sppre1;
volatile int *spi_sptdat1;
volatile int *spi_sprdat1;
#define SPI_TXRX_READY (((*spi_spsta1)&0x1) == 0x1)
/************************************************ *********/
static const struct file_operations spi_fops =
{
.owner=THIS_MODULE,
.open=spi_open,
.read=spi_read,
.ioctl=spi_ioctl,
.release=spi_release,
.write=spi_write,
};
/************************************************ *******/
static int spi_open(struct inode *inode,struct file *filp)
{
filp->private_data =&spiCdev;
/**********************************************
*PCLK
************************************************/
/*control PCLK into spi block*/
*s3c2440_clkcon |= 0x40000;
printk("s3c2440_clkcon=%08Xn",*s3c2440_clkcon);
/**********************************************
*GPG PORTS
************************************************/
/*config SCK1,MOSI1,MISO1 = 11*/
*spi_gpgcon |= 0x0000FC00;
/*poll up MISO1 MOSI1,SCK1*/
*spi_gpgup &=0xFF1F;
*spi_gpgup |= 0x0060;
/**********************************************
*GPF PORTS
************************************************/
*spi_gpfcon &= 0xFCF3;
*spi_gpfcon |= 0x0108;
*spi_gpfup &= 0xED;
*spi_gpfdat |= 0x10;
/**********************************************
*SPI REGS
************************************************/
//SPI Baud Rate Prescaler Register,Baud Rate=PCLK/2/(Prescaler value+1)
*spi_sppre1=0x18; //freq = 1M
printk("spi_sppre1=%02Xn",*spi_sppre1);
//polling,en-sck,master,low,format A,nomal = 0 | TAGD = 1
*spi_spcon1=(0<<6)|(0<<5)|(1<<4)|(1<<3)|(0<<2)|(0<<1)|(0<<0 );
printk("spi_spcon1=%02Xn",*spi_spcon1);
//Multi Master error detect disable,reserved,rech=*spi_sprdat0;lease
*spi_sppin1=(0<<2)|(0<<0);
printk("spi_sppin1=%02Xn",*spi_sppin1);
return 0;
}
static int spi_release(struct inode *inode,struct file *filp)
{
//free irq
free_irq(IRQ_EINT1, NULL);
printk("<1>releasen");
return 0;
}
static void writeByte(const char c)
{
int j = 0;
*spi_sptdat1 = c;
for(j=0;j<0xFF;j++);
while(!SPI_TXRX_READY)
for(j=0;j<0xFF;j++);
}
static char readByte(void)
{
int j = 0;
char ch = 0;
*spi_sptdat1 = (char)loopChar;
for(j=0;j<0xFF;j++);
while(!SPI_TXRX_READY)
for(j=0;j<0xFF;j++);
ch=*spi_sprdat1;
return ch;
}
static ssize_t spi_read(struct file *filp,char __user *buf,size_t count,loff_t *f_ops)
{
//int len=0;
char ch;
printk("<1>spi read!n");
ch=readByte();
copy_to_user(buf,&ch,1);
return 1;
}
static ssize_t spi_write(struct file *filp,const char __user *buf,size_t count,loff_t *f_ops)
{
int i;
char *kbuf;
printk("<1>spi write!,count=%dn",count);
kbuf=kmalloc(count,GFP_KERNEL);
if(copy_from_user(kbuf,buf,count))
{
printk("no enough memory!n");
return -1;
}
for(i=0;i
{
writeByte(*kbuf);
printk("write 0x%02X!n",*kbuf);
kbuf++;
}
return count;
}
static ssize_t spi_ioctl(struct inode *inode,struct file *filp,unsigned int cmd,unsigned long data)
{
return 0;
}
static int __init spi_init(void)
{
int result;
dev_t devno = MKDEV(spi_major, 0);
/**/
if (spi_major)
result = register_chrdev_region(devno, 1, spi_name);
else /**/
{
result = alloc_chrdev_region(&devno, 0, 1, spi_name);
spi_major = MAJOR(devno);
}
if (result < 0)
return result;
cdev_init(&spiCdev, &spi_fops);
spiCdev.owner = THIS_MODULE;
spiCdev.ops = &spi_fops;
if (cdev_add(&spiCdev, devno, 1))
printk(KERN_NOTICE "Error adding spi %d", 0);
s3c2440_clkcon = (int *)ioremap(0x4C00000c,3);
spi_gpgcon = (int *)ioremap (0x56000060,4);
spi_gpgdat = (int *)ioremap (0x56000064,2);
spi_gpgup = (int *)ioremap (0x56000068,2);
spi_gpfcon = (int *)ioremap (0x56000050,2);
spi_gpfdat = (int *)ioremap (0x56000054,1);
spi_gpfup = (int *)ioremap (0x56000058,1);
spi_spcon1 = (int *)ioremap(0x59000020,1);
spi_spsta1 = (int *)ioremap(0x59000024,1);
spi_sppin1 = (int *)ioremap(0x59000028,1);
spi_sppre1 = (int *)ioremap(0x5900002c,1);
spi_sptdat1 = (int *)ioremap(0x59000030,1);
spi_sprdat1 = (int *)ioremap(0x59000034,1);
printk("Init spi success!n");
return result;
}
static void __exit spi_exit(void)
{
cdev_del(&spiCdev);
unregister_chrdev_region(MKDEV(spi_major, 0), 1);
printk("<1>spi_exit!n");
}
module_init(spi_init);
module_exit(spi_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("nkzc");
MODULE_DESCRIPTION("SPI driver for S3C2440");
A few points to note:
1. At first, the void unregister_chrdev(unsigned int major, const char *name) function was used in the spi_exit() function to unregister the device. However, when the driver was insmoded again, it prompted "Device or resource busy". After changing to unregister_chrdev_region(), everything was normal. This shows that even if only one device is registered, register_chrdev_region() and unregister_chrdev_region() must be used together.
2. When defining register variables such as spi_spcon1, add the volatile keyword in front of it, so that each time the variable is accessed, the CPU will read the value from the actual memory instead of using the value in the register. Especially the spi_spsta1 variable, its lowest bit represents whether the spi is ready to send or receive. If there is no volatile, it may cause an infinite loop in the readByte() or writeByte() function.
3. The module_param() macro is used to pass parameters to the driver. Here, an int type loopChar parameter is defined. When loading the module, insmod mini2440_spi.ko loopChar=123 is used to set the value of loopChar.
Test program: spi_test.c
#include
#include
#include
#include
#include
#include
#include
int main(int argc, char **argv)
{
int fd;
int count=0;
char buf[]={0x11,0x22,0x33,0x44,0x55};
fd = open("/dev/mini2440_spi", O_RDWR);
if (fd < 0) {
perror("open device spi");
exit(1);
}
count=write(fd,buf,sizeof(buf)/sizeof(buf[0]));
read(fd,buf,1);
printf("read byte is: 0x%02Xn",buf[0]);
close(fd);
return 0;
}
This is a very simple program that calls the open, write, read, and close functions respectively. You can observe the output results to verify whether the driver is correct. The output of read is the value of loopChar.
Note: When opening, pay attention to the second parameter flag. Only when flag is O_RDWR, the corresponding spi_read and spi_write functions in the driver will be called.
Previous article:linux-2.6.32 porting I2C-EEPROM driver on mini2440 development board
Next article:ARM9 S3C2440 Timer Interrupt
Recommended ReadingLatest update time:2024-11-16 09:45
- Popular Resources
- Popular amplifiers
- 100 Examples of Microcontroller C Language Applications (with CD-ROM, 3rd Edition) (Wang Huiliang, Wang Dongfeng, Dong Guanqiang)
- Real-time driver monitoring system via modal and viewpoint analysis
- Raspberry Pi Development in Action (2nd Edition) ([UK] Simon Monk)
- A Practical Tutorial on ASIC Design (Compiled by Yu Xiqing)
Professor at Beihang University, dedicated to promoting microcontrollers and embedded systems for over 20 years.
- Innolux's intelligent steer-by-wire solution makes cars smarter and safer
- 8051 MCU - Parity Check
- How to efficiently balance the sensitivity of tactile sensing interfaces
- What should I do if the servo motor shakes? What causes the servo motor to shake quickly?
- 【Brushless Motor】Analysis of three-phase BLDC motor and sharing of two popular development boards
- Midea Industrial Technology's subsidiaries Clou Electronics and Hekang New Energy jointly appeared at the Munich Battery Energy Storage Exhibition and Solar Energy Exhibition
- Guoxin Sichen | Application of ferroelectric memory PB85RS2MC in power battery management, with a capacity of 2M
- Analysis of common faults of frequency converter
- In a head-on competition with Qualcomm, what kind of cockpit products has Intel come up with?
- Dalian Rongke's all-vanadium liquid flow battery energy storage equipment industrialization project has entered the sprint stage before production
- Allegro MicroSystems Introduces Advanced Magnetic and Inductive Position Sensing Solutions at Electronica 2024
- Car key in the left hand, liveness detection radar in the right hand, UWB is imperative for cars!
- After a decade of rapid development, domestic CIS has entered the market
- Aegis Dagger Battery + Thor EM-i Super Hybrid, Geely New Energy has thrown out two "king bombs"
- A brief discussion on functional safety - fault, error, and failure
- In the smart car 2.0 cycle, these core industry chains are facing major opportunities!
- The United States and Japan are developing new batteries. CATL faces challenges? How should China's new energy battery industry respond?
- Murata launches high-precision 6-axis inertial sensor for automobiles
- Ford patents pre-charge alarm to help save costs and respond to emergencies
- New real-time microcontroller system from Texas Instruments enables smarter processing in automotive and industrial applications
- Share some of my experiences as a part-time communication designer
- TI application note: Wi-Fi built-in security
- EEWORLD University Hall----Live Replay: Energy Saving and Carbon Reduction-Omron Relay, Switch, and Connector Solutions for Photovoltaic Inverter/Energy Storage Systems
- Nonlinear Compensation Method for Sensor Bridge Circuit
- DIY_boostxl-k350qvg under the recent proofing storm
- Today is 520, another beautiful day for confession. What actions will you take?
- Design and Implementation of Digital Video Conversion Interface Based on FPGA
- Creative Electronics: DIY Wireless Charger for Your Phone
- Specifications of several types of batteries
- [Lazy people take care of fish tank control system] Debug method in ON Semiconductor IDE environment