rtos下的iic驱动

iic

(Inter-Integrated Circuit Bus)内部集成电路总线
用处:常用于微控制器与外设之间的通信。

1、硬件接法:

如下图所示,主控芯片引出两条线SCL,SDA线,在一条I2C总线上可以接很多I2C设备。数据可以从主设备传到从设备上,从设备也能传数据到主设备上,即双向传输。
在这里插入图片描述

常用设备:eeprom, pca9555(串转并), RTC实时时钟
注意:iic本身没有拉高的能力,需要接外接电阻才能拉高。

2、收发信号:(主机写从机读)

1、主设备发start
刚开始主芯片要发出一个start信号,然后发出一个设备地址(用来确定是往哪一个芯片写数据),读/写(0表示写,1表示读)。
回应(用来确定这个设备是否存在),然后就可以传输数据,传输数据之后,要有一个回应信号(确定数据是否接受完成),然后再传输下一个数据。
每传输一个数据,接受方都会有一个回应信号,数据发送完之后,主芯片就会发送一个停止信号。如下图:白色为主设备到从设备传输,灰色为从设备传输到主设备。
在这里插入图片描述

传输是以8位为单元数据传输的,先传输最高位(MSB),主芯片发出start信号之后,然后发出9个时钟传输数据。(初始化后SDA和SCL线都处于高电平状态)
(1)开始信号(S):SCL为高电平时,SDA高电平向低电平跳变,开始传送数据。
(2)结束信号(P):SCL为高电平时,SDA由低电平向高电平跳变,结束传送数据。
(3)响应信号(ACK):接收器在接收到8位数据后,在第9个时钟周期,拉低SDA。(第九个时钟周期的SDA电平信号由接收方决定)
SDA上传输的数据必须在SCL为高电平期间保持稳定,SDA上的数据只能在SCL为低电平期间变化。如图:
在这里插入图片描述

I2C通常应该在时钟的高电平而不是上升沿或者下降沿读取数据,IC抽样判决的时候只要求在时钟上升沿阶段SDA要能够稳定的保持一段时间。
I2C要求SDA在SCL为高电平不能跳变,是因为SCL为高电平时,会触发I2C的起始条件和停止条件。
其实I2C的采样也是边沿采样,只不过是上下沿都采一次,这样做的目的是为了检测起始和终止信号,I2C规定:SCL线为高电平期间,SDA线由高电平向低电平的变化表示起始信号;SCL线为高电平期间,SDA线由低电平向高电平的变化表示终止信号。
因此,必须保证在每个时钟周期内对数据线SDA采样两次。

在这里插入图片描述

3、响应ACK和非响应NACK(Not Acknowledge)

每个字节传输必须带响应位,相关的响应时钟也由主机产生,在响应的时钟脉冲期间(第9个时钟周期),
发送端释放SDA线,接收端把SDA拉低。
以下情况会导致出现NACK位:
1.接收机没有发送机响应的地址,接收端没有任何ACK发送给发射机
2.由于接收机正在忙碌处理实时程序导致接无法接收或者发送
3.传输过程中,接收机识别不了发送机的数据或命令
4.接收机无法接收
5.主机接收完成读取数据后,要发送NACK结束告知从机

4、主机读,从机写:

读数据与写数据相似,但读数据会多几个步骤。要想从从机读取数据,首先要知道从机地址以及寄存器地址,这两部需要进行读操作来实现,和读操作一致。读操作完成后,主机发送重复开始+从机地址+R/W=1(读操作,设置为1),从机返回ACK,此时主机释放SDA线转由从机控制,主机读取SDA总线进行数据接收,每发送1 byte数据,主机会响应ACK表示还需要再接收数据。当主机接收完想要的数据后,主机将会返回NACK,告诉从机释放SDA总线,随后主机发送STOP命令,将总线释放,完成读操作。如下图示意:

在这里插入图片描述

主机:start+从机地址+写(00xae
从机:ack
主机:寄存器地址			//有可能要两次,看eeprom大小
从机:ack
主机:restart+从机地址+(1,指运行从机写)0xaf
从机:ack + 发送8bit数据
主机:NACK+stop

实际读取过程中,使用以上步骤,可能存在以下问题:

操作:主机设在接收N之后,再设置ack = 0。
实际情况:再接N后,ACK还没设置成功,ack=1已经发出去,且上一个发	出去的ack标志也是1,再设nack之后,此时DR中的数据为N+1,移位寄存器	中的数据为N+2.
结论:
经过测试不影响最终传输的准确性(数据正确)和完整性(等的到busy=0,且下一轮收发正常)
最早设ack的时机:可以再接收到N-2之后(即count_remain=3,接收完成后),直接设ack为0.
最早设stop的时机:等btf=1,设置stop_flag=1,DT=N-1。(此时N-1在DT中,N在移位寄存器中,所以不存在少收的情况)

总结:

主从机制。SCL始终由主设备控制。SDA谁用谁控制(单双共,串行)。
带校验,低速率,可靠。
默认高电平。
正常情况:高电平稳定,低电平采样。(可以看做采两次)
(不正常的高电平跳变正好作为开始和结束信号)
开始:SCL高电平,SDA拉低。
接收:SCL高电平,SDA拉高。
所谓低电平采样:SCL低电平期间,对SDA的电平进行采样。

SCM下驱动

创建:

iic时钟使能
SCL,SDA对应GPIO时钟使能
对应DMA时钟使能
配置iic工作模式
中断模式下配置中断使能(event中断和err中断)

Iic工作模式:

    priv->param.clk_speed         = MS_I2C_CLK_SPEED_STANDARD;
    priv->param.duty_cycle        = MS_I2C_DUTY_CYCLE_2;
    priv->param.own_address1      = 0U;
    priv->param.addressing_mode   = MS_I2C_ADDRESSING_MODE_7B;
    priv->param.dual_address_mode = MS_I2C_DUAL_ADDRESS_DISABLE;
	priv->param.own_address2      = 0U;
	
速度100K,400K,1M,3.4M
快速模式下的CLK低高电平时间占比:2/116/9。(低电平周期/高电平周期)
自身地址寄存器:一般不用
地址模式:7位或10位。
双字节寻址模式:10位时,需要使用。

传输流程以中断模式为例:

static ms_ssize_t __scm_i2c_bus_trans_it(privinfo_t *priv, const ms_i2c_msg_t *msg, ms_size_t n_msg)
{
    ms_ssize_t i   = 0;
    ms_err_t   err = 0;

    if (priv->sembid == 0U) {
        err = ms_semb_create("i2c_semb", MS_FALSE, MS_WAIT_TYPE_PRIO, &priv->sembid);
        if(err != MS_ERR_NONE) {
            ms_printk(MS_PK_ERR, "%s, %d, ms_semb_create error: %d\n", __FILE__, __LINE__, err);
            return i;
        }
    }

    for (i = 0; i < n_msg; i++, msg++) {

        priv->msg          = msg;
        priv->err_code     = 0U;
        priv->pbuffer      = msg->buf;
        priv->count_remain = msg->len;

        if (!(priv->msg->flags & MS_I2C_M_NOSTART)) {
            I2C_GenerateSTART((I2C_Type *)(priv->base), ENABLE);
            err = __scm_i2c_check_event_wait((I2C_Type *)(priv->base), I2C_EVENT_MASTER_START_GENERATED, SUCCESS);
            if (err != MS_ERR_NONE) {
                ms_printk(MS_PK_ERR, "%s %d : check event wait timeout!\r\n", __func__, __LINE__);
                break;
            }
        }

        if (msg->flags & MS_I2C_M_READ) {
            I2C_PECPositionConfig((I2C_Type *)(priv->base), I2C_PECPosition_Current);
            if (msg->len > 1) {
                I2C_AcknowledgeConfig((I2C_Type *)(priv->base), ENABLE);
            } else {
                I2C_AcknowledgeConfig((I2C_Type *)(priv->base), DISABLE);
            }
        }

        if (I2C_GetFlagStatus((I2C_Type *)(priv->base), I2C_FLAG_STARTF) == RESET) {
            I2C_Cmd((I2C_Type *)(priv->base), ENABLE);
        }

        I2C_INTConfig((I2C_Type *)(priv->base), I2C_INT_BUF | I2C_INT_EVT | I2C_INT_ERR, ENABLE);

        if (ms_semb_wait(priv->sembid, MS_TICK_PER_SEC) != MS_ERR_NONE) {
            ms_printk(MS_PK_DEBUG, "%s, %d, wait semb fail\n", __FILE__, __LINE__);
            break;
        }

        if (!(priv->msg->flags & MS_I2C_M_NOSTOP)) {
            /*
             * wait until BUSY clear
             */
            while (I2C_GetFlagStatus((I2C_Type *)(priv->base), I2C_FLAG_BUSYF));
        }
    }

    return i;
}

event中断的处理

void scm_i2c_event_irqhandle(ms_uint8_t channel)
{
    privinfo_t  *priv     = __scm_i2c_bus[channel - 1].ctx;
    I2C_Type    *instance = (I2C_Type *)(priv->base);
    ms_uint16_t  sts1     = instance->STS1;
    ms_uint16_t  ctl2     = instance->CTRL2;

    /*
     * handle start flag
     */
    if ((I2C_GetINTStatus(instance, I2C_INT_STARTF) != RESET))
    {
        __scm_handle_start_flag(priv);
        return;
    }

    /*
     * handle addr10 flag, Must use sts1 and ctl2 read when the interrupt is first entered
     */
    if ((GET_STS1_ADDR10F(sts1) == SET) && (GET_CTL2_EVT(ctl2) == SET)) {
        __scm_handle_addr10_flag(priv);
        return;
    }

    /*
     * handle addr flag, Must use sts1 and ctl2 read when the interrupt is first entered
     */
    if ((GET_STS1_ADDRF(sts1) == SET) && (GET_CTL2_EVT(ctl2) == SET)) {
        __scm_handle_addr_flag(priv);
        return;
    }

    /*
     * handle tde flag
     */
    if ((I2C_GetINTStatus(instance, I2C_INT_TDE) != RESET))
    {
        __scm_handle_tde_flag(priv);
        return;
    }

    /*
     * handle rdne flag
     */
    if ((I2C_GetINTStatus(instance, I2C_INT_RDNE) != RESET))
    {
        __scm_handle_rdne_flag(priv);
        return;
    }
}

err中断的处理

void scm_i2c_error_irqhandle(ms_uint8_t channel)
{
    privinfo_t  *priv     = __scm_i2c_bus[channel - 1].ctx;
    I2C_Type    *instance = (I2C_Type *)priv->base;
    ms_uint32_t  error    = I2C_ERROR_NONE;

    /*
     * handle bus_err flag
     */
    if ((I2C_GetINTStatus(instance, I2C_INT_BUSERR) != RESET))
    {
        error |= I2C_ERROR_BERR;
        I2C_ClearITPendingBit(instance, I2C_INT_BUSERR);
    }

    /*
     * handle ack_err flag
     */
    if ((I2C_GetINTStatus(instance, I2C_INT_ACKFAIL) != RESET)){
        I2C_ClearITPendingBit(instance, I2C_INT_ACKFAIL);
        error |= I2C_ERROR_AF;
        I2C_GenerateSTOP(instance, ENABLE);
    }

    /*
     * handle over_run/under_run err flag
     */
    if ((I2C_GetINTStatus(instance, I2C_INT_OVRUN) != RESET))
    {
        error |= I2C_ERROR_OVR;
        I2C_ClearITPendingBit(instance, I2C_INT_OVRUN);
    }

    /*
     * set err_code and close interrupt
     */
    if (error != I2C_ERROR_NONE)
    {
        priv->err_code |= error;
        I2C_NACKPositionConfig(instance, I2C_NACKPosition_Current);
        I2C_INTConfig(instance, I2C_INT_BUF | I2C_INT_EVT | I2C_INT_ERR, DISABLE);
    }
}

注意点:

在这里插入图片描述

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值