⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 saa1064_rpxcllf.c

📁 《嵌入式Linux-硬件
💻 C
字号:
/* * SAA1064_rpxcllf v1.0 11/10/01 * www.embeddedlinuxinterfacing.com * * The original location of this code is * http://www.embeddedlinuxinterfacing.com/chapters/10/ * * Copyright (C) 2001 by Craig Hollabaugh * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Library 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 * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this program; if not, write to the * Free Software Foundation, Inc., * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *//* * SAA1064_rpxcllf.c is based on procfs_example.c by Erik Mouw. * For more information, please see The Linux Kernel Procfs Guide, Erik Mouw * http://kernelnewbies.org/documents/kdoc/procfs-guide/lkprocfsguide.html **//* SAA1064_rpxcllf * This device driver demonstrates I2C communication with a SAA1064 LED * display driver. The RPX-CLLF's MPC860 port B is used for I2C data (SDA) * and clock (SCL) signals. This routine doesn't use the MPC860's I2C * controller but implements a bit-banging algorithm. * * The driver creates a /proc directory entry called * /proc/trailblazer/temperaturedisplay0. Scripts can write values to * temperaturedisplay0 which are then displayed on the LED displays. * * This driver only communicates with a single SAA1064 at I2C bus address 0. *//*powerpc-linux-gcc -O2 -D__KERNEL__ -DMODULE -I/usr/src/powerpc-linux/include  -c SAA1064_rpxcllf.c -o /tftpboot/powerpc-rootfs/tmp/SAA1064_rpxcllf.o*/#include <linux/module.h>#include <linux/kernel.h>#include <linux/init.h>#include <linux/proc_fs.h>#include <asm/uaccess.h>#include <asm/io.h>#include <linux/delay.h>#include <asm/8xx_immap.h>#define MODULE_VERSION "1.0"#define MODULE_NAME "SAA1064_rpxcllf"#define SDA   0x00000001#define SCL   0x00000002#define DELAY 5#define SAA1064ADDRESS 0x70volatile immap_t *immap;/* references * see section 34.3 Port B MPC860 PowerQUICC User's Manual * * For more SAA1064 or I2C protocol, visit the Philips Semiconductor website * at http://www.semiconductors.philips.com */static struct proc_dir_entry  *tb_dir,                              *temperaturedisplay0_file;/* here are the I2C signaling macros */#define SCLLow()  immap->im_cpm.cp_pbdat &= ~SCL#define SCLHigh() immap->im_cpm.cp_pbdat |=  SCL#define SDALow()  immap->im_cpm.cp_pbdat &= ~SDA#define SDAHigh() immap->im_cpm.cp_pbdat |=  SDA#define readSDA() (SDA == (immap->im_cpm.cp_pbdat & SDA))#define readSCL() (SCL == (immap->im_cpm.cp_pbdat & SCL))/* * function startCommunication * This function sets SDA and SCL in the idle state then * initiates the 'start' condition */void startCommunication(void){  SDAHigh();      /* put SDA in idle state    */  SCLHigh();      /* put SCL in idle state    */  udelay(DELAY);  /* let lines settle         */  SDALow();       /* initiate start condition */  udelay(DELAY);  /* let lines settle         */  SCLLow();       /* initiate start condition */}/* * function stopCommunication * This function sets SDA and SCL in an known state then * initiates the 'stop' condition */void stopCommunication(void){  SCLLow();       /* put SCL in known state  */  SDALow();       /* put SDA in known state  */  udelay(DELAY);  /* let lines settle        */  SCLHigh();      /* initiate stop condition */  SDAHigh();      /* initiate stop condition */}/* * function receiveByte * This function toggles the clock line while reading * the transmitted data bits. I2C communications sends * MSB first. */unsigned char receiveByte(void){  unsigned char i, b;  SDAHigh();                 /* this tri-states SDA                      */  b = 0;  for (i = 0; i < 8; i++)  {    udelay(DELAY);            /* let lines settle                        */    SCLHigh();                /* send the clock                          */    udelay(DELAY);            /* let lines settle                        */    b = (b << 1) | readSDA(); /* shift the bits then OR the incoming bit */    SCLLow();                 /* send the clock                          */  }  /* this sets up for the next incoming byte */  udelay(DELAY);  SCLHigh();  udelay(DELAY*2);  SCLLow();  return b;}/* * function sendByte * This function toggles the clock line while transmitting * data bits. I2C communications sends MSB first. This function * allows monitors the acknowledge bit (bit 9) asserted by the * receiver. */unsigned char sendByte(unsigned char b){  unsigned char i;  for (i = 0; i < 8; i++)  {    if (0x80 == (b & 0x80)) /* is the MSB 0 or 1? */      SDAHigh();    else      SDALow();    udelay(DELAY);          /* let lines settle   */    SCLHigh();              /* send the clock     */    udelay(DELAY);          /* let lines settle   */    SCLLow();               /* send the clock     */    b = (b << 1);           /* shift to the left  */  }  /* this sets up for the next outgoing byte      */  udelay(DELAY);  SDAHigh();  SCLHigh();  udelay(DELAY*2); /* 2 delays help you see acks on a scope */  /* read the ack here, a sent ack is a 0. */  i = readSDA();  SCLLow();  return i;}/* * function writei2c * This function accepts an address, a character buffer and buffer length. * It starts communication, sends the address, then the buffer. * If an ack error occurs, it tells you (well it prints to the console). */unsigned char writei2c(unsigned address, unsigned char *buffer,                        unsigned length){  unsigned char i, error;  startCommunication();  error = sendByte(address & 0xFE);    /* 0xFE? The last bit of the I2C address is a read/nWrite bit */  if (error) /* didn't get an ack at the address */  {    stopCommunication();    printk("no ack at address 0x%2X\n",address);    return error;  }/* sending the buffer here */  for (i = 0; i < length; i++)  {    error = sendByte(buffer[i]);    if (error) /* didn't get an ack for that byte sent */    {      stopCommunication();      printk("no ack at buffer byte %d\n",i);      return error;    }  }/* we're done */  stopCommunication();  return 0;}/* * function readi2c * This function accepts an address, a character buffer and buffer length. * It starts communication, sends the address, then reads the reply into  * buffer. If an ack error occurs, it tells you (well it prints to the  * console). */unsigned char readi2c(unsigned address, unsigned char *buffer,                       unsigned length){  unsigned char i, error;  startCommunication();  error = sendByte(address | 0x01);  if (error) /* didn't get an ack at the address */  {    stopCommunication();    printk("no ack at address 0x%2X\n",address);    return error;  }  /* receiving bytes for the buffer here */  for (i = 0; i < length; i++)  {    buffer[i] = receiveByte();  }/* we're done */  stopCommunication();  return 0;}/* * function proc_write_display * This function gets called when the user writes something to * /proc/trailblazer/temperaturedisplay0. It contains a mapping array * from numbers (0-9,a-f) to what segments to turn on */static int proc_write_temperaturedisplay0(struct file *file,                                           const char *buffer,                                          unsigned long count, void *data){  unsigned char e, displaybuffer[5];/* seg is a segment mapping table. Element 0, 0xFC, tells the * SAA1064 to turn on the segments to display a 0. Likewise, * seg's other entries map to 1 through 9 and a through f. */  unsigned char seg[] = { 0xFC, 0x60, 0xDA, 0xF2, 0x66,                          0xB6, 0xBE, 0xE0, 0xFE, 0xF6,                          0xEE, 0x3E, 0x9C, 0x7A, 0x9E, 0x8E } ;  if (count >= 4)  {    displaybuffer[0] = 0x01;    displaybuffer[1] = seg[buffer[0]-'0']; /* subtracting '0' shifts the   */    displaybuffer[2] = seg[buffer[1]-'0']; /* ascii numbers the user wrote */    displaybuffer[3] = seg[buffer[2]-'0']; /* to the device file to their  */    displaybuffer[4] = seg[buffer[3]-'0']; /* numeric equivalent           */    e = writei2c(SAA1064ADDRESS, displaybuffer, 5);/* for debugging    printk("proc_write_display = %d\n",e); */  }  return 1;}/* * function init_SAA1064_rpxcllf * This function creates the /proc directory entries: trailblazer and * trailblazer/temperaturedisplay0. It find the IMMR then configures * PB30 and PB31 as general I/O, open drain and outputs. * It initializes the SAA1064 and has it displays the middle segment '-' * as a sign that the driver loaded successfully. */static int __init init_SAA1064_rpxcllf(void){  unsigned char e,buffer[5];  int rv = 0;/* Create the trailblazer /proc entry */  tb_dir = proc_mkdir("trailblazer", NULL);  if(tb_dir == NULL) {          rv = -ENOMEM;          goto out;  }  tb_dir->owner = THIS_MODULE;/* Create temperaturedisplay0 and make it readable by all - 0444 */  temperaturedisplay0_file = create_proc_entry("temperaturedisplay0", 0444,                                               tb_dir);  if(temperaturedisplay0_file == NULL) {          rv = -ENOMEM;          goto no_temperaturedisplay0;  }  temperaturedisplay0_file->data = NULL;  temperaturedisplay0_file->read_proc = NULL;  temperaturedisplay0_file->write_proc = &proc_write_temperaturedisplay0;  temperaturedisplay0_file->owner = THIS_MODULE;  /* get the IMMR */  immap = (immap_t *)(mfspr(IMMR) & 0xFFFF0000);  /* make PB30 and PB31 general I/O */  immap->im_cpm.cp_pbpar &= ~SDA;  immap->im_cpm.cp_pbpar &= ~SCL;  /* make PB30 and PB31 open drain */  immap->im_cpm.cp_pbodr |= SDA;  immap->im_cpm.cp_pbodr |= SCL;  /* make PB30 and PB31 outputs    */  immap->im_cpm.cp_pbdir |= SDA;  immap->im_cpm.cp_pbdir |= SCL;  /* display a little info for the happy module loader */  printk("immr    = 0x%08X\n",immap);  printk("PBPAR   = 0x%04X\n",immap->im_cpm.cp_pbpar);  printk("PBDIR   = 0x%04X\n",immap->im_cpm.cp_pbdir);  printk("PBODR   = 0x%04X\n",immap->im_cpm.cp_pbodr);  printk("PBDAT   = 0x%04X\n",immap->im_cpm.cp_pbdat);  buffer[0] = 0x00; /* internal starting SAA1064 register address */  buffer[1] = 0x46; /* 12mA current, all digits on, dynamic mode  */  buffer[2] = 0x02; /* turn on '-' segment on each display        */  buffer[3] = 0x02;  buffer[4] = 0x02;  buffer[5] = 0x02;  if (writei2c(SAA1064ADDRESS, buffer, 6))  {    printk("Display initialization failed\n");    rv = -EREMOTEIO;    goto no_temperaturedisplay0;  }  else    printk("Display initialization passed\n");/* everything initialized */  printk(KERN_INFO "%s %s initialized\n",MODULE_NAME, MODULE_VERSION);  return 0;no_temperaturedisplay0:  remove_proc_entry("temperaturedisplay0", tb_dir);out:  return rv;}/* * function cleanup_SAA1064_rpxcllf * This function turns off the SAA1064 display to show that the * driver unloaded. It then removes the /proc directory entries */static void __exit cleanup_SAA1064_rpxcllf(void){  unsigned char buffer[5];  buffer[0] = 0x00;  buffer[1] = 0x00; /* configuration reg, turn displays off */  writei2c(SAA1064ADDRESS, buffer, 2);  remove_proc_entry("temperaturedisplay0", tb_dir);  remove_proc_entry("trailblazer", NULL);  printk(KERN_INFO "%s %s removed\n", MODULE_NAME, MODULE_VERSION);}module_init(init_SAA1064_rpxcllf);module_exit(cleanup_SAA1064_rpxcllf);MODULE_AUTHOR("Craig Hollabaugh");MODULE_DESCRIPTION("SAA1064 driver for RPX-CLLF");EXPORT_NO_SYMBOLS;

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -