Chinaunix首页 | 论坛 | 博客
  • 博客访问: 247501
  • 博文数量: 43
  • 博客积分: 1878
  • 博客等级: 上尉
  • 技术积分: 457
  • 用 户 组: 普通用户
  • 注册时间: 2010-06-02 11:08
文章分类

全部博文(43)

文章存档

2011年(3)

2010年(40)

分类: LINUX

2010-06-11 14:25:59

刚开始搞嵌入式驱动开发,接受了第一个小的任务,通过对S3C2440中几个寄存器的读写,实现与DS3234之间的通信,很简单,因为以前从来没有接触过,花了我两周时间。
在s3c2440datasheet中,告知的编程步骤如下:
1. Set Baud Rate Prescaler Register (SPPREn).
2. Set SPCONn to configure properly the SPI module.
3. Write data 0xFF to SPTDATn 10 times in order to initialize MMC or SD card.
4. Set a GPIO pin, which acts as nSS, low to activate the MMC or SD card.
5. Tx data i Check the status of Transfer Ready flag (REDY=1), and then write data to SPTDATn.
6. Rx data(1): SPCONn's TAGD bit disable = normal mode
7. i write 0xFF to SPTDATn, then confirm REDY to set, and then read data from Read Buffer.
8. Rx data(2): SPCONn's TAGD bit enable = Tx Auto Garbage Data mode
9. i confirm REDY to set, and then read data from Read Buffer (then automatically start to transfer).
10. Set a GPIO pin, which acts as nSS, high to deactivate the MMC or SD card.

1,配置IO脚为SPI接口
  用SPIO通道和GPE脚。其中涉及GPECON、GPEDAT、GPEUP三个寄存器
2,时钟信号寄存器
  CLKCON,设置其中的第18位为SPI时钟
3,SPI寄存器配置
  s3c2440有两个SPI通道,因为我们只选用了SPI0,故涉及的寄存器有SPCON0(SPI控制寄存器)、SPPRE0(SPI传送频率控制寄存器)、SPSTA0(SPI状态控制寄存器)、SPTDAT0(SPI传输数据寄存器)、SPRDAT0(SPI读数据寄存器)共5个寄存器,其中SPPRE0,设置传送频率(即SPI_CLK = PCLK/2/(SPPRE+1),PCLK = 48M)
注意:写SPCON寄存器时,一定要检查CLKCON的状态,CLKCON寄存器位要激活。

#include <stdio.h>
#include <time.h>
#include <pthread.h>
#include <fcntl.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include <sys/time.h>

/* macro function */

#define BCD2BIN(val)            (((val)&0x0f) + ((val>>4)*10))
#define BIN2BCD(val)            ((((val)/10)<<4) + (val%10))
#define bcd2bin(val)            BCD2BIN(val)
#define bin2bcd(val)            BIN2BCD(val)

#define DS3234_REG_SECONDS        0x00
#define DS3234_REG_MINUTES        0x01
#define DS3234_REG_HOURS        0x02
#define DS3234_REG_DAY            0x03
#define DS3234_REG_DATE            0x04
#define DS3234_REG_MONTH        0x05
#define DS3234_REG_YEAR            0x06
#define DS3234_REG_CENTURY        (1 << 7) /* Bit 7 of the Month registert */


#define DS3234_REG_CONTROL        0x0E
#define DS3234_REG_CONT_STAT    0x0F

/* debug switch */
#define ds3234_debug

int fd = -1;

/* unkown define */
#define REG8(addr)                (*(volatile unsigned char *)(addr))            
#define REG16(addr)                (*(volatile unsigned short *)(addr))
#define REG32(addr)                (*(volatile unsigned int *)(addr))

unsigned int clk_v_addr;
unsigned int gpe_v_addr;
unsigned int spi_v_addr;


#define GPECON                    REG32(gpe_v_addr + 0x40) /* Port E control, Configure the pins of port E, reset value 0x0 */
#define GPEDAT                    REG16(gpe_v_addr + 0x44) /* Port E data, The data register for port E, reset value Undef */
#define GPEUP                     REG16(gpe_v_addr + 0x48) /* Pull-up control E, Pull-up disable register for port E, reset value 0x0000 */

#define CLKCON                    REG32(clk_v_addr + 0x0C) /* clock generator control */

#define SPCON0                    REG8(spi_v_addr) /* SIP control */
#define SPPRE0                    REG8(spi_v_addr + 0x0c) /* SPI baud rate proscaler */
#define SPSTA0                    REG8(spi_v_addr + 0x04) /* SPI channel 0 status register, reset value 0x0 */                    
#define SPTDAT0                    REG8(spi_v_addr + 0x10) /* SPI Tx data */
#define SPRDAT0                    REG8(spi_v_addr + 0x14) /* SPI channel 0 Rx data register, reset value 0xff */

pthread_mutex_t timeMutex = PTHREAD_MUTEX_INITIALIZER;

/*
extern void rtctimeShow(void);
extern void rtctimeReset(void);
extern int rtc2sys(void);
void *mmap(void *start, size_t length ,int port, int flags, int fd, off_t offset);
int munmap(void *start, size_t length);
*/


static int p2v_mmap()
{

    unsigned int clk_p_addr = 0x4c000000;
    unsigned int gpe_p_addr = 0x56000000;
    unsigned int spi_p_addr = 0x59000000;

    printf("[%d]p2v_mmap start\n", __LINE__);
    fd = open("/dev/mem", O_RDWR|O_SYNC);
    if(fd < 0){
        printf("Error open /dev/men!\n");
        return -1;
    }    

    clk_v_addr = (int)mmap(NULL, 256, PROT_READ|PROT_WRITE, MAP_SHARED, fd, clk_p_addr&(~0x000000ff));
    if(clk_v_addr < 0){
        printf("Unable to mmap %08x\n", clk_p_addr);
        return -1;
    }else{
        printf("map p_addr %08x to %08x\n", clk_p_addr, clk_v_addr);
    }
    
    gpe_v_addr = (int)mmap(NULL, 256, PROT_READ|PROT_WRITE, MAP_SHARED, fd, gpe_p_addr&(~0x000000ff));
    if(gpe_v_addr < 0){
        printf("Unable to mmap %08x\n", gpe_p_addr);
        return -1;
    }else{
        printf("map p_addr %08x to %08x\n", gpe_p_addr, gpe_v_addr);
    }

    spi_v_addr = (int)mmap(NULL, 256, PROT_READ|PROT_WRITE, MAP_SHARED, fd, spi_p_addr&(~0x000000ff));
    if(spi_v_addr < 0){
        printf("Unable to mmap %08x\n", spi_p_addr);
        return -1;
    }else{
        printf("map p_addr %08x to %08x\n", spi_p_addr, spi_v_addr);
    }
    printf("[%d]p2v_mmap end\n", __LINE__);
}

static void spi_port0_init(void)
{
    int i;

    printf("*******************************************\n");
    printf("[%d]%s register value before set\n", __LINE__, __FILE__);    
    printf("[%d]GPEUP value %04x\n", __LINE__, GPEUP);
    printf("[%d]GPECON value %08x\n", __LINE__, GPECON);
    printf("[%d]GPEDAT value %04x\n", __LINE__, GPEDAT);
    printf("[%d]CLKCON value %08x\n", __LINE__, CLKCON);
    printf("[%d]SPPRE0 value %02x\n", __LINE__, SPPRE0);
    printf("[%d]SPCON0 value %02x\n", __LINE__, SPCON0);
    printf("[%d]SPTDAT0 value %02x\n",__LINE__, SPTDAT0);    
    printf("*******************************************\n\n");

    /* configure pull-up */
    GPEUP &= ~(0x3800);
    GPEUP |= 0x2004;
    /* set GPE
        GPE2[5:4] = SIPCS0
        GPE11[23:22] = SPIMISO0
         GPE12[25:24] = SPIMOSO0
        GPE13[27:26] = SPICLK
    */

    
    GPECON = ((GPECON & 0xf03fffcf) | 0xa800010);
    
    /* config GPE Pin UP&CLOW */
    GPEDAT |= 0x4;
    SPPRE0 = 0x7;
    SPCON0 = (0<<5) | (1<<4) | (1<<3) |(1<<2) | (1<<1) | (0<<0);

    /* control PCLK into SPI block */
    CLKCON |= (0x1 << 18);

    for(i = 0; i < 10; i++)
        SPTDAT0 = 0xff;

    printf("*******************************************\n");
    printf("[%d]%s register value after setted\n", __LINE__, __FILE__);    
    printf("[%d]GPEUP value %08x\n", __LINE__, GPEUP);
    printf("[%d]GPECON value %08x\n", __LINE__, GPECON);
    printf("[%d]GPEDAT value %08x\n", __LINE__, GPEDAT);
    printf("[%d]CLKCON value %08x\n", __LINE__, CLKCON);
    printf("[%d]SPPRE0 value %02x\n", __LINE__, SPPRE0);
    printf("[%d]SPCON0 value %02x\n", __LINE__, SPCON0);
    printf("[%d]SPTDAT0 value %02x\n",__LINE__, SPTDAT0);    
    printf("*******************************************\n");
}

static void ncs_enable(int enable)
{
    unsigned char tmp;

    if(enable){
        tmp = GPEDAT; /* GPEDAT is the data register for port E */
        tmp &=~(0x4);
        GPEDAT = tmp;    
    }else{
        tmp = GPEDAT;
        tmp |= 0x4;
        GPEDAT = tmp;
    }
}

static void spi_poll_done(void)
{
    /* 等待SPSTA0末尾为1 */
    while(!(SPSTA0 & 0x1));
}

static void spi_tx_data(unsigned char data)
{
    spi_poll_done();
    SPTDAT0 = data;
    spi_poll_done();
}


static unsigned char ds_read(unsigned char addr)
{
    unsigned char data;
    
    /* 片选 */
    ncs_enable(1);

    spi_tx_data(addr);
    data = SPRDAT0;
    spi_tx_data(0xff);
    data = SPRDAT0;
    spi_poll_done();

    ncs_enable(0);
    
    return data;
}

static void ds_write(unsigned char addr, unsigned char data)
{
    ncs_enable(1);
    
    spi_tx_data(addr);
    spi_tx_data(data);
    
    ncs_enable(0);
}

static int ds3234_get_reg(unsigned char addr, unsigned char *pdata)
{
    *pdata = ds_read(addr);
    return 0;
}

static int ds3234_set_reg(unsigned char addr, unsigned char data)
{
    ds_write(addr|0x80, data);
    return 0;
}

static void ds3234_init(void)
{
    unsigned char tmp;

    printf("[%d]%s ds3234_init\n", __LINE__, __FILE__);
    /* 初始化端口0 */
    spi_port0_init();

    get_mutex();
    /* 初始化contrl寄存器 */
    ds3234_get_reg(DS3234_REG_CONTROL, &tmp);
    printf("[%d]Control Reg:0x%02x\n", __LINE__, tmp);
    ds3234_set_reg(DS3234_REG_CONTROL, tmp&0x1c);

    /* 初始化control_state寄存器 */
    ds3234_get_reg(DS3234_REG_CONT_STAT, &tmp);
    printf("[%d]Control State Reg:0x%02x\n", __LINE__, tmp);
    ds3234_set_reg(DS3234_REG_CONT_STAT, tmp&0x88);


#ifdef ds3234_debug
    ds3234_get_reg(DS3234_REG_CONTROL, &tmp);
    printf("Control Reg:0x%02x\n", tmp);
    ds3234_get_reg(DS3234_REG_CONT_STAT, &tmp);
    printf("Control State Reg:0x%02x\n", tmp);
#endif    
    put_mutex();
}

int ds3234_probe(void)
{
    int retval;
    unsigned char data;

    ds3234_init();
    get_mutex();
    ds3234_set_reg(0x98, 0x0); /* the addr 0x98 for SRAM write's Address */
    ds3234_set_reg(0x99, 0xaa); /* the addr 0x99 for SRAM write's data */
    ds3234_set_reg(0x98, 0x0);
    ds3234_get_reg(0x19, &data); /* 0x19, the address for SRAM read data*/
    
    printf("[%d]%s data = 0x%02x\n", __LINE__, __FILE__, data);
    if(data == 0xaa)
    {
        retval = 1;
    }
    else
    {
        retval = 0;
    }
    printf("[%d]%s retval = %d\n", __LINE__, __FILE__, retval);
    put_mutex();

    return retval;        
}

int get_mutex(void)
{
    if(pthread_mutex_lock(&timeMutex) != 0)
    {
        perror("pthread_mutex_lock, faild\n");
        return -1;
    }
    return 0;
}

int put_mutex(void)
{
    if(pthread_mutex_unlock(&timeMutex) != 0)
    {
        perror("pthread_mutex_unlock,faild\n");
        return -1;
    }
    return 0;
}

static int rtcDateGet(struct tm *tm)
{
    if(ds3234_probe()){
        ds3234_get_datetime(tm);
    }
    return 0;
 }

int ds3234_get_datetime(struct tm *ptime)
{
    unsigned char data;
    get_mutex();
    ds3234_get_reg(DS3234_REG_SECONDS, &data);
    data = bcd2bin(data);
    ptime->tm_sec = data;
    
    ds3234_get_reg(DS3234_REG_MINUTES, &data);
    data = bcd2bin(data);
    ptime->tm_min = data;
    
    ds3234_get_reg(DS3234_REG_HOURS, &data);
    data = bcd2bin(data & 0x3f);
    ptime->tm_hour = data;
    
    ds3234_get_reg(DS3234_REG_DAY, &data);
    data = bcd2bin(data);
    ptime->tm_wday = data;
    
    ds3234_get_reg(DS3234_REG_DATE, &data);
    data = bcd2bin(data);
    ptime->tm_mday = data;
    
    ds3234_get_reg(DS3234_REG_MONTH, &data);
    data = bcd2bin(data & 0x1f) - 1;
    ptime->tm_mon = data;
    
    ds3234_get_reg(DS3234_REG_YEAR, &data);
    data = bcd2bin(data&0xff);
    if(data < 74)
        data += 100;
    ptime->tm_year = data;
    
#ifdef ds3234_debug
    printf("\n Read RTC values:\n");
    printf("tm_sec: %d\n", ptime->tm_sec);
    printf("tm_min: %d\n", ptime->tm_min);
    printf("tm_hour: %d\n", ptime->tm_hour);
    printf("tm_wday: %d\n", ptime->tm_wday);
    printf("tm_mday: %d\n", ptime->tm_mday);
    printf("tm_mon: %d\n", ptime->tm_mon);
    printf("tm_year: %d\n", ptime->tm_year);
#endif    
    put_mutex();
    return 0;
}

static int ds3234_set_datetime(struct tm *ptime)
{
    get_mutex();
#ifdef ds3234_debug
    printf("\n Set RTC values:\n");
    printf("tm_sec: %d\n", ptime->tm_sec);
    printf("tm_min: %d\n", ptime->tm_min);
    printf("tm_hour: %d\n", ptime->tm_hour);
    printf("tm_wday: %d\n", ptime->tm_wday);
    printf("tm_mday: %d\n", ptime->tm_mday);
    printf("tm_mon: %d\n", ptime->tm_mon);
    printf("tm_year: %d\n", ptime->tm_year);
#endif
    ds3234_set_reg(DS3234_REG_SECONDS, bin2bcd(ptime->tm_sec));
    ds3234_set_reg(DS3234_REG_MINUTES, bin2bcd(ptime->tm_min));
    ds3234_set_reg(DS3234_REG_HOURS, bin2bcd(ptime->tm_hour));
    if(ptime->tm_wday == 0)
        ptime->tm_wday = 9;
    ds3234_set_reg(DS3234_REG_DAY, bin2bcd(ptime->tm_wday));
    ds3234_set_reg(DS3234_REG_DATE, bin2bcd(ptime->tm_mday));
    ds3234_set_reg(DS3234_REG_MONTH, bin2bcd(ptime->tm_mon + 1));
    ds3234_set_reg(DS3234_REG_YEAR, bin2bcd(ptime->tm_year));    
    put_mutex();

    return 0;
}

static void rtc_time_show(void)
{
    printf("[%d]%s rtc_time_show\n", __LINE__, __FILE__);
    
    struct tm ctime;
    char tmbuf[128];
    ds3234_get_datetime(&ctime);
    strftime(tmbuf, 128, "%Y-%m-%d %H:%M:%S week %w", &ctime);
    printf("RTC time: %s\n", tmbuf);
}


void rtcShow(void)
{
    if(ds3234_probe())
    {
        rtc_time_show();
    }
}

static void rtc_time_reset(void)
{
    struct tm ctime;
    printf("Please input current date/time:\n");
    printf("    year:");
    scanf("%d", &ctime.tm_year);
    printf("    month:");
    scanf("%d", &ctime.tm_mon);
    printf("    day:");
    scanf("%d", &ctime.tm_mday);
    printf("    hours:");
    scanf("%d", &ctime.tm_hour);
    printf("    minute:");
    scanf("%d", &ctime.tm_min);
    printf("    second:");
    scanf("%d", &ctime.tm_sec);
    
    ctime.tm_wday = 0;
    ctime.tm_mon -= 1;
    ds3234_set_datetime(&ctime);
}

void rtcReset(void)
{
    if(ds3234_probe())
    {
        rtc_time_reset();
    }
}

int main(int argc,char * argv[])
{
    p2v_mmap();
    
    rtcShow();
    printf("************************************\n");
    rtc_time_reset();
    printf("************************************\n");    
    rtc_time_show();
    printf("************************************\n");
    
#if 0
    printf("\nMENU\n");
    printf("1.read the register value\n");
    printf("2.change the register value\n");
    printf("Please select the number:\n");

    /* scanf("%d\n", &i); */
    printf("i = %d\n", i);

    switch(i)
    {
    case 1:
        printf("before into rtcshow()\n");
        rtcShow();
        break;
    case 2:
        rtcReset();
        break;
    default:
        break;
    }
#endif    
    return 0;
}

因为是应用程序访问内核寄存器,所以需要mmap(),在使用mmap(),过程中由于经验不足,花了很多时间。
mmap(),中offset与lenght是需要注意。
阅读(3513) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~