Cortex-M3 (NXP LPC1788)之IIC控制器

I2C通信协议在上篇文章中进行了介绍,下面对LPC1788的I2C控制器进行分析。要使用I2C模块,需要配置相应的寄存器,如功率、时钟、管脚等。以I2C0为例,配置P0.27和P0.28管脚分别为SDA和SCL功能,在PCONP中使能I2C0功率控制,根据PCLK和要使用的I2C的频率设置SCLH和SCLL。

I2C模块可以工作在主发送、主接收、从发送、从接收四种工作模式。开发板上LPC1788工作在主发送模式时发送数据给总线上的外设。工作在主接收模式时接收总线上的外设数据。

主发送模式

在这里插入图片描述

操作流程如下:

1,发送一个S起始条件。初始化控制位寄存器,清除STA、STO、SI,然后向I2C控制置位寄存器I2CONSET中置位STA位,一旦总线空闲,I2C逻辑会马上测试I2C总线并产生一起始条件。如果起始条件得到总线应答,中断标志SI会置位,通读取I2C状态寄存器I2STAT的值,如果为0x08表示可以进入发送数据的操作。

2,第一个发送的字节包含接收设备的从机地址和数据方向位。方向位(R/W)设置成0,表示进行些操作,将该字节写入I2C数据寄存器I2CDAT进行发送。发送成功后会产生一个应答,并且SI再次置位,读取I2STAT中的状态,如果是0x18表示得到正确的应答,可以进行数据的传输。写入从机地址后,应该清零起始条件中置位的STA位,并且SI位必须在串行传输继续之前复位。

3,最后进行数据的发送,每个数据发送完成都会有一个应答,且SI置位,如果发送成功读取的STAT的值为0x28。要注意的是在下一次串行传输前必须复位SI。

4,数据发送完成后,发送一个P停止条件。在I2CONSET中置位STO标志,并且清除STA和SI标志。

至此,主发送模式结束。

主接收模式
在这里插入图片描述

操作流程如下:

1,发送一个起始条件S。和主发送模式相同。

2,写第一个字节数据,操作过程和主发送相同,只需将方向位改成读,并且正确的应答标志为0x40。

3,数据接收时需要注意的是,接收的最后一个字节总是非应答,这样主机就可以告诉从机读序列已经完成。如果接收到的应答A,则I2CSTAT的值为0x50。接收到非答,则I2CSTAT的值为0x58,。应答或者非应答,通过设置SI位进行控制。

4,在接收到非应答的状态标志0x58后,发送一个停止条件。

主发送切换主接收

我们可以单独设置主发送和主接收,也可以再主接收或主发送数据传输结束后,选择不发送停止条件P,而是发送重复起始条件,进行主发送和主接收的切换。

在这里插入图片描述
操作流程如下:

1,在主发送模式数据传输完成后,不发送停止条件。即上面介绍的主发送模式流程的第4步不执行,而是改成重新发送一个起始标志,置位STA。重复起始发送成功后返回 的状态标志位0x10。

2,切换成功后即可按主接收步骤中的第二步直接开始执行。

到这里LPC1788的主工作模式介绍完了。

IIC应用实例–PCA9532进行IO扩展和LED亮度控制

PCA9532是一个I2C接口的设备,可以用于IO的扩展和LED的亮度调节。它内部集成了振荡器,可以输出2路用户可编程的PWM波,周期从6.58ms到1.69S。16路的输出,可以设置成输出高低电平以及PWM波输出。

作为从设备,他的8位地址的高四位固定为1100,最低位为数据的方向位,剩下的3位有硬件连线确定他的地址。PCA9532共有10个寄存器来配置他的输出状态。

在这里插入图片描述
其中INPUT0 INPUT1在管脚配置成普通IO时候用于读入IO脚的状态。PSC0 PWM0 PSC1 PWM1用于设置两路PWM波的周期和占空比。LS0~LS3用于选择每个管脚的功能,包括通用LED OFF、LED ON、 PWM0、 PWM1。

知道了需要配置的寄存器,那怎么通过I2C通信来配置这几个寄存器呢?当LPC1788发出PCA9532的地址得到应答后,需要发送一个字节的数据用于配置控制寄存器,他们第四位为B3B0位,比如发送的字节第4位为0,即B3B0为0则他接下去收到的数据用来配置INPUT0。配置寄存器的第4位为AI,即autoincrease,表示接收到一个字节的配置数据后,是否自动的将B3~B0加1,方便配置下一个表中的寄存器。

开发板上的PCA9532的电路图如下

在这里插入图片描述

程序中配置LED07为GPIO用于检测按键,LED8LED11配置成PWM输出,将LED RED做出渐亮渐暗的效果,LED12~LED15根据按键值设置成LED ON 或LED OFF。按键值读取PCA9532的INPUT0得到。程序如下:

#define PCLK    60000000
#define I2C0SCK  100000
#define PCA9532_ADDRESS 0x60
 
#define rI2C0CONSET	(*(volatile unsigned*)(0x4001C000))
#define rI2C0CONCLR	(*(volatile unsigned*)(0x4001C018))
#define rI2C0STAT	(*(volatile unsigned*)(0x4001C004))
#define rI2C0DAT	(*(volatile unsigned*)(0x4001C008))
#define rI2C0SCLH	(*(volatile unsigned*)(0x4001C010))
#define rI2C0SCLL	(*(volatile unsigned*)(0x4001C014))
 
#define rIOCON_P0_27	(*(volatile unsigned *)(0x4002C06C))
#define rIOCON_P0_28	(*(volatile unsigned *)(0x4002C070))
 
#define rPCONP	    (*(volatile unsigned*)(0x400FC0C4))
 
unsigned char config[11], read_data[1];
void I2C0_Init()
{
    rIOCON_P0_27 = (rIOCON_P0_27&(~0x7))|0x1;   //I2C0_SDA
    rIOCON_P0_28 = (rIOCON_P0_28&(~0x7))|0x1;   //I2C0_SCL
    rPCONP |= 0x1<<7;   //I2C0 Power Enable 
    rI2C0SCLH = PCLK/I2C0SCK/2;                 //set I2C0 frequency 100khz
    rI2C0SCLL = PCLK/I2C0SCK/2;
    rI2C0CONSET |= 0x1<<6;                       //I2C接口使能
    rI2C0CONCLR = 0x1<<3|0x1<<5;                //清除SI STA 
}
 
unsigned char I2C0_Start()
{
    rI2C0CONCLR = 0x1<<3;               //清除SI标志
    
    rI2C0CONSET |= 0x1<<5;              //置位STA进入主发送模式
    
    while(!(rI2C0CONSET&(0x1<<3)));     //起始条件发送完成
    
    rI2C0CONCLR = 0x1<<5;               //清除STA标志
    
    return (rI2C0STAT&0xF8);
}
 
void I2C0_Stop()
{
    rI2C0CONCLR = 0x1<<5;               //清除STA标志
    rI2C0CONSET |= 0x1<<4;              //发送STO标志
    rI2C0CONCLR = 0x1<<3;               //清除SI标志
}
 
unsigned char I2C0_SentByte(unsigned char data)
{
    rI2C0DAT = data;
    
    rI2C0CONCLR = 0x1<<3;               //清除SI标志
    
    while(!(rI2C0CONSET&(0x1<<3)));     //发送完数据得到了应答 
 
    return (rI2C0STAT&0xF8);
}
 
unsigned char I2C0_GetByte(unsigned char* data, unsigned char ack_flag)
{
    if(ack_flag)
    {
        rI2C0CONSET |= 0x1<<2;              //主接收模式,接收到一个字节返回应答
    }
    else
    {
        rI2C0CONCLR = 0x1<<2;               //主接收模式,接收最后一个字节时,不返回应答
    }
    rI2C0CONCLR = 0x1<<3;                   //清除SI标志
    while(!(rI2C0CONSET&(0x1<<3)));         //发送完数据得到了应答 
    *data = (unsigned char)rI2C0DAT;
    return (rI2C0STAT&0xF8);
}
 
int I2C0_MasterTransfer(unsigned char slave_address, unsigned char *transfer_data, unsigned int transfer_count,\
                    unsigned char *receive_data, unsigned int receive_count)
{
    unsigned char status;
    unsigned int i;
   
    status = I2C0_Start();
    while(status != 0x08);
    
    status = I2C0_SentByte(slave_address<<1);
    while(status != 0x18);
    
    for(i=0; i<transfer_count; i++)
    {
        status = I2C0_SentByte(*(transfer_data+i));
        while(status != 0x28);
    }
   
    if(receive_data!=(void*)0 && receive_count!=0)
    {
        //进入主接收模式
        status = I2C0_Start();
        while(status != 0x10);
        
        status = I2C0_SentByte((slave_address<<1)|0x1);
        while(status != 0x40);
        
        for(i=0; i<receive_count; i++)
        {
            if(i<receive_count-1)
            {
                status = I2C0_GetByte(receive_data, 1);
                while(status != 0x50);
            }
            else
            {
                status = I2C0_GetByte(receive_data, 0);
                while(status != 0x58);
            }
            receive_data++;
        }
    }
 
 
    I2C0_Stop();
    
    return 1;
}
 
void PCA9532_Config()
{
    config[0] = 0x1<<4;     //读写控制寄存器后低四位自动增加
    config[1] = 0;          //input0
    config[2] = 0;          //input1
    config[3] = 0;          //PSC0    PWM0的周期6.5ms
    config[4] = 0;          //PWM0    PWM0占空比设置成0%
    config[5] = 0;          //PSC1    PWM1的周期为6.5ms
    config[6] = 0;          //PWM1    PWM1占空比设置成0%
    config[7] = 0;          //LS0
    config[8] = 0;          //LS1     LED0~7 设置成GPIOS
    config[9] = 0xFA;       //LS2     11111010, LED8,9->blinks PWM0; LED10,11->blinks PWM1
    config[10] = 0;         //LS3     LED12~LED15, LED off
 
}
 
 
int main(void)
{
    unsigned char flag=1, data=0;
    unsigned int i;
    I2C0_Init();
    PCA9532_Config();
    while(1)
    {
        I2C0_MasterTransfer(PCA9532_ADDRESS, config, sizeof(config), 0, 0);
        
        I2C0_MasterTransfer(PCA9532_ADDRESS, &data, 1, read_data, 1);        
        
        if(flag)
        {
            config[4]++;
            config[6]++;
        }
        else
        {
            config[4]--;
            config[6]--;
        }
        
        if(config[4]==255 || config[4]==0)
        {
            flag = !flag;
        }
        
        for(i=0; i<4; i++)
        {
            if(read_data[0]&(0x1<<i))
            {
                config[10] &= ~(0x3<<(i*2));
                
            }
            else
            {
                config[10] |= (0x1<<(i*2));
            }
        }
        
    }
}

程序调试过程中遇到如下问题,要注意:

  • I2C控制清除寄存器为只读,因此不能进行 |= 操作,否则状态寄存器的值异常。不知道为什么不是产生异常复位,之前EEPROM也对只读寄存器进行该操作会产生系统异常进入异常中断。
  • 在主发送模式切换到主接收模式的过程中,一定要先清除SI标志。开始没注意,发送重复起始表示后的状态一直是0x28,把这个重复起始标志单数据发送。

本文章转载自 Cortex-M3 (NXP LPC1788)之IIC控制器

  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值