ABOV M0系列开发:M0S10系列_M0S10系列I2C通信协议应用

M0S10系列I2C通信协议应用

I2C通信协议简介

I2C(Inter-Integrated Circuit)是一种简单的双向两线制同步串行总线,用于连接多个低速外围设备。I2C总线由两根信号线组成:SCL(Serial Clock Line,串行时钟线)和SDA(Serial Data Line,串行数据线)。I2C协议支持多主设备和多从设备,每个设备都有一个唯一的地址,通过地址可以进行数据的读写操作。
在这里插入图片描述

I2C通信的基本概念

  • SCL线:串行时钟线,用于同步数据传输,由主设备控制。
  • SDA线:串行数据线,用于传输数据,可以由主设备或从设备控制。
  • 起始信号:当SCL线为高电平时,SDA线从高电平变为低电平,表示通信开始。
  • 停止信号:当SCL线为高电平时,SDA线从低电平变为高电平,表示通信结束。
  • 应答信号:每个字节传输结束后,接收设备会在第9个时钟周期发送一个应答信号,表示已经成功接收数据。

I2C通信的时序

I2C通信的时序图如下:

SCL:  _____ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __
      |   |   |   |   |   |   |   |   |   |   |   |   |   |   |   |
      |___|___|___|___|___|___|___|___|___|___|___|___|___|___|___|___

SDA:  _____ _______ _______ _______ _______ _______ _______ _______
      |   |       |       |       |       |       |       |       |
      |___|_______|_______|_______|_______|_______|_______|_______|___

      START  ADDR   R/W   ACK   DATA   ACK   STOP
  • 起始信号:SCL为高电平时,SDA由高变低。
  • 地址:主设备发送从设备的7位地址(或10位地址)。
  • 读写位:主设备发送一个读写位,0表示写,1表示读。
  • 应答信号:从设备发送一个应答信号,表示已经接收到地址和读写位。
  • 数据传输:主设备和从设备之间传输数据,每个字节传输后都有一个应答信号。
  • 停止信号:SCL为高电平时,SDA由低变高。

M0S10系列I2C模块概述

M0S10系列单片机内置了I2C模块,支持I2C主模式和从模式。I2C模块的主要功能包括:

  • 主模式:作为I2C总线的主设备,可以发起通信并控制时钟线。
  • 从模式:作为I2C总线的从设备,可以响应主设备的通信请求。
  • 中断支持:支持I2C通信中的各种中断,便于处理通信事件。
  • DMA支持:支持直接内存访问,提高数据传输效率。

I2C模块的寄存器

M0S10系列单片机的I2C模块包含多个寄存器,用于配置和控制I2C通信。主要寄存器包括:

  • I2C_CR1:控制寄存器1,用于配置I2C模块的基本功能。
  • I2C_CR2:控制寄存器2,用于配置I2C模块的高级功能。
  • I2C_OAR1:自己的地址寄存器1,用于设置主模式下的从设备地址。
  • I2C_OAR2:自己的地址寄存器2,用于设置从模式下的从设备地址。
  • I2C_DR:数据寄存器,用于读取和写入I2C数据。
  • I2C_SR1:状态寄存器1,用于读取I2C通信的状态。
  • I2C_SR2:状态寄存器2,用于读取I2C通信的高级状态。

I2C模块的配置

在使用I2C模块之前,需要进行一系列的配置,包括时钟配置、模式配置、地址配置和中断配置。

时钟配置

首先,需要配置I2C模块的时钟。M0S10系列单片机的I2C模块时钟源通常为APB1时钟。可以通过以下代码配置I2C时钟:

// 使能I2C1时钟
RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C1, ENABLE);
模式配置

M0S10系列单片机的I2C模块可以配置为主模式或从模式。通过设置I2C_CR1寄存器的PE(Peripheral Enable)位和SMBUS位来选择模式。

// 配置I2C1为主模式
I2C_DeInit(I2C1);
I2C_InitTypeDef I2C_InitStructure;
I2C_InitStructure.I2C_Mode = I2C_Mode_I2C;
I2C_InitStructure.I2C_DutyCycle = I2C_DutyCycle_2;
I2C_InitStructure.I2C_OwnAddress1 = 0x00;
I2C_InitStructure.I2C_Ack = I2C_Ack_Enable;
I2C_InitStructure.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit;
I2C_InitStructure.I2C_ClockSpeed = 100000; // 100kHz
I2C_Init(I2C1, &I2C_InitStructure);

// 使能I2C1
I2C_Cmd(I2C1, ENABLE);
地址配置

在主模式下,需要设置从设备的地址。在从模式下,需要设置自己的地址。通过设置I2C_OAR1寄存器来配置地址。

// 配置I2C1的从设备地址为0x50
I2C_InitStructure.I2C_OwnAddress1 = 0x50;
I2C_Init(I2C1, &I2C_InitStructure);
中断配置

可以通过配置I2C_CR1寄存器的中断使能位来启用I2C中断。同时,需要在NVIC中配置相应的中断通道。

// 使能I2C1中断
I2C_ITConfig(I2C1, I2C_IT_EVT | I2C_IT_ERR | I2C_IT_BUF, ENABLE);

// 配置I2C1中断通道
NVIC_InitTypeDef NVIC_InitStructure;
NVIC_InitStructure.NVIC_IRQChannel = I2C1_EV_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);

NVIC_InitStructure.NVIC_IRQChannel = I2C1_ER_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);

I2C主模式通信

在主模式下,M0S10系列单片机可以发起I2C通信,并控制时钟线。主模式通信包括读操作和写操作。

I2C主模式写操作

在主模式下写数据时,需要生成起始信号、发送从设备地址、发送数据、生成停止信号。以下是一个简单的I2C主模式写操作示例:

#include "stm32f10x.h"

void I2C1_Config(void) {
    // 使能I2C1时钟
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C1, ENABLE);

    // 配置I2C1为主模式
    I2C_DeInit(I2C1);
    I2C_InitTypeDef I2C_InitStructure;
    I2C_InitStructure.I2C_Mode = I2C_Mode_I2C;
    I2C_InitStructure.I2C_DutyCycle = I2C_DutyCycle_2;
    I2C_InitStructure.I2C_OwnAddress1 = 0x00;
    I2C_InitStructure.I2C_Ack = I2C_Ack_Enable;
    I2C_InitStructure.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit;
    I2C_InitStructure.I2C_ClockSpeed = 100000; // 100kHz
    I2C_Init(I2C1, &I2C_InitStructure);

    // 使能I2C1
    I2C_Cmd(I2C1, ENABLE);

    // 使能I2C1中断
    I2C_ITConfig(I2C1, I2C_IT_EVT | I2C_IT_ERR | I2C_IT_BUF, ENABLE);

    // 配置I2C1中断通道
    NVIC_InitTypeDef NVIC_InitStructure;
    NVIC_InitStructure.NVIC_IRQChannel = I2C1_EV_IRQn;
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
    NVIC_Init(&NVIC_InitStructure);

    NVIC_InitStructure.NVIC_IRQChannel = I2C1_ER_IRQn;
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2;
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
    NVIC_Init(&NVIC_InitStructure);
}

void I2C1_Write(uint8_t slave_addr, uint8_t reg_addr, uint8_t* data, uint16_t length) {
    // 生成起始信号
    I2C_GenerateSTART(I2C1, ENABLE);

    // 等待起始信号完成
    while (!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_MODE_SELECT)) {}

    // 发送从设备地址
    I2C_Send7bitAddress(I2C1, slave_addr, I2C_Direction_Transmitter);

    // 等待从设备地址被应答
    while (!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED)) {}

    // 发送寄存器地址
    I2C_SendData(I2C1, reg_addr);

    // 等待寄存器地址被应答
    while (!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_TRANSMITTED)) {}

    // 发送数据
    for (uint16_t i = 0; i < length; i++) {
        I2C_SendData(I2C1, data[i]);
        while (!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_TRANSMITTED)) {}
    }

    // 生成停止信号
    I2C_GenerateSTOP(I2C1, ENABLE);
}

// 中断服务例程
void I2C1_EV_IRQHandler(void) {
    // 处理事件中断
    if (I2C_GetITStatus(I2C1, I2C_IT_EVT) != RESET) {
        // 处理事件
        I2C_ClearITPendingBit(I2C1, I2C_IT_EVT);
    }
}

void I2C1_ER_IRQHandler(void) {
    // 处理错误中断
    if (I2C_GetITStatus(I2C1, I2C_IT_ERR) != RESET) {
        // 处理错误
        I2C_ClearITPendingBit(I2C1, I2C_IT_ERR);
    }
}

int main(void) {
    // 初始化I2C1
    I2C1_Config();

    // 配置GPIO
    GPIO_InitTypeDef GPIO_InitStructure;
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6 | GPIO_Pin_7;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_OD;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOB, &GPIO_InitStructure);

    uint8_t data[2] = {0x01, 0x02};
    uint8_t slave_addr = 0x50;
    uint8_t reg_addr = 0x00;

    // 写数据
    I2C1_Write(slave_addr, reg_addr, data, 2);

    while (1) {
        // 主循环
    }
}

I2C主模式读操作

在主模式下读数据时,需要生成起始信号、发送从设备地址、发送寄存器地址、生成重复起始信号、读取数据、生成停止信号。以下是一个简单的I2C主模式读操作示例:

#include "stm32f10x.h"

void I2C1_Config(void) {
    // 使能I2C1时钟
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C1, ENABLE);

    // 配置I2C1为主模式
    I2C_DeInit(I2C1);
    I2C_InitTypeDef I2C_InitStructure;
    I2C_InitStructure.I2C_Mode = I2C_Mode_I2C;
    I2C_InitStructure.I2C_DutyCycle = I2C_DutyCycle_2;
    I2C_InitStructure.I2C_OwnAddress1 = 0x00;
    I2C_InitStructure.I2C_Ack = I2C_Ack_Enable;
    I2C_InitStructure.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit;
    I2C_InitStructure.I2C_ClockSpeed = 100000; // 100kHz
    I2C_Init(I2C1, &I2C_InitStructure);

    // 使能I2C1
    I2C_Cmd(I2C1, ENABLE);

    // 使能I2C1中断
    I2C_ITConfig(I2C1, I2C_IT_EVT | I2C_IT_ERR | I2C_IT_BUF, ENABLE);

    // 配置I2C1中断通道
    NVIC_InitTypeDef NVIC_InitStructure;
    NVIC_InitStructure.NVIC_IRQChannel = I2C1_EV_IRQn;
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
    NVIC_Init(&NVIC_InitStructure);

    NVIC_InitStructure.NVIC_IRQChannel = I2C1_ER_IRQn;
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2;
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
    NVIC_Init(&NVIC_InitStructure);
}

uint8_t I2C1_Read(uint8_t slave_addr, uint8_t reg_addr) {
    uint8_t data = 0;

    // 生成起始信号
    I2C_GenerateSTART(I2C1, ENABLE);

    // 等待起始信号完成
    while (!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_MODE_SELECT)) {}

    // 发送从设备地址(写方向)
    I2C_Send7bitAddress(I2C1, slave_addr, I2C_Direction_Transmitter);

    // 等待从设备地址被应答
    while (!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED)) {}

    // 发送寄存器地址
    I2C_SendData(I2C1, reg_addr);

    // 等待寄存器地址被应答
    while (!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_TRANSMITTED)) {}

    // 生成重复起始信号
    I2C_GenerateSTART(I2C1, ENABLE);

    // 等待重复起始信号完成
    while (!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_MODE_SELECT)) {}

    // 发送从设备地址(读方向)
    I2C_Send7bitAddress(I2C1, slave_addr, I2C_Direction_Receiver);

    // 等待从设备地址被应答
    while (!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED)) {}

    // 读取数据
    I2C_AcknowledgeConfig(I2C1, DISABLE); // 禁用应答
    I2C_GenerateSTOP(I2C1, ENABLE); // 生成停止信号
    data = I2C_ReceiveData(I2C1);

    // 等待数据接收完成
    while (!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_RECEIVED)) {}

    // 重新启用应答
    I2C_AcknowledgeConfig(I2C1, ENABLE);

    return data;
}

// 中断服务例程
void I2C1_EV_IRQHandler(void) {
    // 处理事件中断
    if (I2C_GetITStatus(I2C1, I2C_IT_EVT) != RESET) {
        // 处理事件
        I2C_ClearITPendingBit(I2C1, I2C_IT_EVT);
    }
}

void I2C1_ER_IRQHandler(void) {
    // 处理错误中断
    if (I2C_GetITStatus(I2C1, I2C_IT_ERR) != RESET) {
        // 处理错误
        I2C_ClearITPendingBit(I2C1, I2C_IT_ERR);
    }
}

int main(void) {
    // 初始化I2C1
    I2C1_Config();

    // 配置GPIO
    GPIO_InitTypeDef GPIO_InitStructure;
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6 | GPIO_Pin_7;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_OD;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOB, &GPIO_InitStructure);

    uint8_t slave_addr = 0x50;
    uint8_t reg_addr = 0x00;

    // 读数据
    uint8_t read_data = I2C1_Read(slave_addr, reg_addr);

    // 处理读取的数据
    // 例如,将数据打印到串口或显示在LED上
    while (1) {
        // 主循环
    }
}

I2C主模式读写操作的注意事项

  1. 起始信号:起始信号必须在SCL线为高电平时生成,SDA线由高变低。
  2. 从设备地址:发送从设备地址时,必须指定读写方向。写操作时使用I2C_Direction_Transmitter,读操作时使用I2C_Direction_Receiver
  3. 寄存器地址:在读写操作中,通常需要先发送寄存器地址,以便从设备知道要读写哪个寄存器。
  4. 应答信号:每个字节传输后,从设备会发送一个应答信号。主设备需要等待应答信号完成后再进行下一步操作。
  5. 重复起始信号:在读操作中,生成重复起始信号是为了在同一通信过程中切换读写方向。
  6. 停止信号:停止信号必须在SCL线为高电平时生成,SDA线由低变高。

I2C主模式的中断处理

中断处理是I2C通信中非常重要的部分,可以提高通信的稳定性和效率。M0S10系列单片机的I2C模块支持多种中断,包括事件中断、错误中断和缓冲区中断。

  • 事件中断:处理I2C通信中的各种事件,如起始信号、地址匹配、数据传输完成等。
  • 错误中断:处理I2C通信中的各种错误,如总线错误、仲裁丢失、应答错误等。
  • 缓冲区中断:处理I2C通信中的数据缓冲区事件,如缓冲区为空或数据准备好。

在中断服务例程中,需要根据不同的中断类型进行相应的处理,确保通信的顺利进行。

I2C主模式的DMA支持

M0S10系列单片机的I2C模块支持DMA(直接内存访问),可以在不占用CPU资源的情况下进行大量数据的传输。使用DMA可以显著提高数据传输效率,特别是在需要传输大量数据的应用中。

配置DMA的基本步骤如下:

  1. 使能DMA时钟:在使用DMA之前,需要使能DMA时钟。
  2. 配置DMA通道:选择合适的DMA通道,并配置传输方向、数据大小、传输模式等。
  3. 配置I2C模块的DMA请求:使能I2C模块的DMA请求,将数据传输任务交给DMA控制器。
  4. 启动DMA传输:在生成起始信号后,启动DMA传输。

以下是一个简单的I2C主模式写操作使用DMA的示例:

#include "stm32f10x.h"

void I2C1_Config(void) {
    // 使能I2C1时钟
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C1, ENABLE);

    // 配置I2C1为主模式
    I2C_DeInit(I2C1);
    I2C_InitTypeDef I2C_InitStructure;
    I2C_InitStructure.I2C_Mode = I2C_Mode_I2C;
    I2C_InitStructure.I2C_DutyCycle = I2C_DutyCycle_2;
    I2C_InitStructure.I2C_OwnAddress1 = 0x00;
    I2C_InitStructure.I2C_Ack = I2C_Ack_Enable;
    I2C_InitStructure.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit;
    I2C_InitStructure.I2C_ClockSpeed = 100000; // 100kHz
    I2C_Init(I2C1, &I2C_InitStructure);

    // 使能I2C1
    I2C_Cmd(I2C1, ENABLE);

    // 使能I2C1中断
    I2C_ITConfig(I2C1, I2C_IT_EVT | I2C_IT_ERR | I2C_IT_BUF, ENABLE);

    // 配置I2C1中断通道
    NVIC_InitTypeDef NVIC_InitStructure;
    NVIC_InitStructure.NVIC_IRQChannel = I2C1_EV_IRQn;
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
    NVIC_Init(&NVIC_InitStructure);

    NVIC_InitStructure.NVIC_IRQChannel = I2C1_ER_IRQn;
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2;
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
    NVIC_Init(&NVIC_InitStructure);
}

void I2C1_DMA_Config(void) {
    // 使能DMA1时钟
    RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);

    // 配置DMA1通道1
    DMA_InitTypeDef DMA_InitStructure;
    DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)(&I2C1->DR);
    DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)(&data[0]);
    DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST;
    DMA_InitStructure.DMA_BufferSize = 2;
    DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
    DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
    DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;
    DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;
    DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;
    DMA_InitStructure.DMA_Priority = DMA_Priority_High;
    DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;
    DMA_Init(DMA1_Channel1, &DMA_InitStructure);

    // 使能DMA1通道1
    DMA_Cmd(DMA1_Channel1, ENABLE);

    // 使能I2C1的DMA请求
    I2C_DMARequestConfig(I2C1, I2C_DMAREquest_Enable);
}

void I2C1_Write_DMA(uint8_t slave_addr, uint8_t reg_addr, uint8_t* data, uint16_t length) {
    // 生成起始信号
    I2C_GenerateSTART(I2C1, ENABLE);

    // 等待起始信号完成
    while (!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_MODE_SELECT)) {}

    // 发送从设备地址
    I2C_Send7bitAddress(I2C1, slave_addr, I2C_Direction_Transmitter);

    // 等待从设备地址被应答
    while (!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED)) {}

    // 发送寄存器地址
    I2C_SendData(I2C1, reg_addr);

    // 等待寄存器地址被应答
    while (!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_TRANSMITTED)) {}

    // 配置DMA
    I2C1_DMA_Config();

    // 使能DMA传输
    I2C_DMACmd(I2C1, ENABLE);

    // 生成停止信号
    while (DMA_GetFlagStatus(DMA1_FLAG_TC1) == RESET) {}
    I2C_GenerateSTOP(I2C1, ENABLE);
}

// 中断服务例程
void I2C1_EV_IRQHandler## I2C主模式通信

在主模式下,M0S10系列单片机可以发起I2C通信,并控制时钟线。主模式通信包括读操作和写操作。

### I2C主模式写操作

在主模式下写数据时,需要生成起始信号、发送从设备地址、发送数据、生成停止信号。以下是一个简单的I2C主模式写操作示例:

```c
#include "stm32f10x.h"

void I2C1_Config(void) {
    // 使能I2C1时钟
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C1, ENABLE);

    // 配置I2C1为主模式
    I2C_DeInit(I2C1);
    I2C_InitTypeDef I2C_InitStructure;
    I2C_InitStructure.I2C_Mode = I2C_Mode_I2C;
    I2C_InitStructure.I2C_DutyCycle = I2C_DutyCycle_2;
    I2C_InitStructure.I2C_OwnAddress1 = 0x00;
    I2C_InitStructure.I2C_Ack = I2C_Ack_Enable;
    I2C_InitStructure.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit;
    I2C_InitStructure.I2C_ClockSpeed = 100000; // 100kHz
    I2C_Init(I2C1, &I2C_InitStructure);

    // 使能I2C1
    I2C_Cmd(I2C1, ENABLE);

    // 使能I2C1中断
    I2C_ITConfig(I2C1, I2C_IT_EVT | I2C_IT_ERR | I2C_IT_BUF, ENABLE);

    // 配置I2C1中断通道
    NVIC_InitTypeDef NVIC_InitStructure;
    NVIC_InitStructure.NVIC_IRQChannel = I2C1_EV_IRQn;
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
    NVIC_Init(&NVIC_InitStructure);

    NVIC_InitStructure.NVIC_IRQChannel = I2C1_ER_IRQn;
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2;
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
    NVIC_Init(&NVIC_InitStructure);
}

void I2C1_Write(uint8_t slave_addr, uint8_t reg_addr, uint8_t* data, uint16_t length) {
    // 生成起始信号
    I2C_GenerateSTART(I2C1, ENABLE);

    // 等待起始信号完成
    while (!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_MODE_SELECT)) {}

    // 发送从设备地址
    I2C_Send7bitAddress(I2C1, slave_addr, I2C_Direction_Transmitter);

    // 等待从设备地址被应答
    while (!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED)) {}

    // 发送寄存器地址
    I2C_SendData(I2C1, reg_addr);

    // 等待寄存器地址被应答
    while (!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_TRANSMITTED)) {}

    // 发送数据
    for (uint16_t i = 0; i < length; i++) {
        I2C_SendData(I2C1, data[i]);
        while (!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_TRANSMITTED)) {}
    }

    // 生成停止信号
    I2C_GenerateSTOP(I2C1, ENABLE);
}

// 中断服务例程
void I2C1_EV_IRQHandler(void) {
    // 处理事件中断
    if (I2C_GetITStatus(I2C1, I2C_IT_EVT) != RESET) {
        // 处理事件
        I2C_ClearITPendingBit(I2C1, I2C_IT_EVT);
    }
}

void I2C1_ER_IRQHandler(void) {
    // 处理错误中断
    if (I2C_GetITStatus(I2C1, I2C_IT_ERR) != RESET) {
        // 处理错误
        I2C_ClearITPendingBit(I2C1, I2C_IT_ERR);
    }
}

int main(void) {
    // 初始化I2C1
    I2C1_Config();

    // 配置GPIO
    GPIO_InitTypeDef GPIO_InitStructure;
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6 | GPIO_Pin_7;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_OD;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOB, &GPIO_InitStructure);

    uint8_t data[2] = {0x01, 0x02};
    uint8_t slave_addr = 0x50;
    uint8_t reg_addr = 0x00;

    // 写数据
    I2C1_Write(slave_addr, reg_addr, data, 2);

    while (1) {
        // 主循环
    }
}

I2C主模式读操作

在主模式下读数据时,需要生成起始信号、发送从设备地址、发送寄存器地址、生成重复起始信号、读取数据、生成停止信号。以下是一个简单的I2C主模式读操作示例:

#include "stm32f10x.h"

void I2C1_Config(void) {
    // 使能I2C1时钟
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C1, ENABLE);

    // 配置I2C1为主模式
    I2C_DeInit(I2C1);
    I2C_InitTypeDef I2C_InitStructure;
    I2C_InitStructure.I2C_Mode = I2C_Mode_I2C;
    I2C_InitStructure.I2C_DutyCycle = I2C_DutyCycle_2;
    I2C_InitStructure.I2C_OwnAddress1 = 0x00;
    I2C_InitStructure.I2C_Ack = I2C_Ack_Enable;
    I2C_InitStructure.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit;
    I2C_InitStructure.I2C_ClockSpeed = 100000; // 100kHz
    I2C_Init(I2C1, &I2C_InitStructure);

    // 使能I2C1
    I2C_Cmd(I2C1, ENABLE);

    // 使能I2C1中断
    I2C_ITConfig(I2C1, I2C_IT_EVT | I2C_IT_ERR | I2C_IT_BUF, ENABLE);

    // 配置I2C1中断通道
    NVIC_InitTypeDef NVIC_InitStructure;
    NVIC_InitStructure.NVIC_IRQChannel = I2C1_EV_IRQn;
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
    NVIC_Init(&NVIC_InitStructure);

    NVIC_InitStructure.NVIC_IRQChannel = I2C1_ER_IRQn;
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2;
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
    NVIC_Init(&NVIC_InitStructure);
}

uint8_t I2C1_Read(uint8_t slave_addr, uint8_t reg_addr) {
    uint8_t data = 0;

    // 生成起始信号
    I2C_GenerateSTART(I2C1, ENABLE);

    // 等待起始信号完成
    while (!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_MODE_SELECT)) {}

    // 发送从设备地址(写方向)
    I2C_Send7bitAddress(I2C1, slave_addr, I2C_Direction_Transmitter);

    // 等待从设备地址被应答
    while (!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED)) {}

    // 发送寄存器地址
    I2C_SendData(I2C1, reg_addr);

    // 等待寄存器地址被应答
    while (!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_TRANSMITTED)) {}

    // 生成重复起始信号
    I2C_GenerateSTART(I2C1, ENABLE);

    // 等待重复起始信号完成
    while (!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_MODE_SELECT)) {}

    // 发送从设备地址(读方向)
    I2C_Send7bitAddress(I2C1, slave_addr, I2C_Direction_Receiver);

    // 等待从设备地址被应答
    while (!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED)) {}

    // 读取数据
    I2C_AcknowledgeConfig(I2C1, DISABLE); // 禁用应答
    I2C_GenerateSTOP(I2C1, ENABLE); // 生成停止信号
    data = I2C_ReceiveData(I2C1);

    // 等待数据接收完成
    while (!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_RECEIVED)) {}

    // 重新启用应答
    I2C_AcknowledgeConfig(I2C1, ENABLE);

    return data;
}

// 中断服务例程
void I2C1_EV_IRQHandler(void) {
    // 处理事件中断
    if (I2C_GetITStatus(I2C1, I2C_IT_EVT) != RESET) {
        // 处理事件
        I2C_ClearITPendingBit(I2C1, I2C_IT_EVT);
    }
}

void I2C1_ER_IRQHandler(void) {
    // 处理错误中断
    if (I2C_GetITStatus(I2C1, I2C_IT_ERR) != RESET) {
        // 处理错误
        I2C_ClearITPendingBit(I2C1, I2C_IT_ERR);
    }
}

int main(void) {
    // 初始化I2C1
    I2C1_Config();

    // 配置GPIO
    GPIO_InitTypeDef GPIO_InitStructure;
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6 | GPIO_Pin_7;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_OD;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOB, &GPIO_InitStructure);

    uint8_t slave_addr = 0x50;
    uint8_t reg_addr = 0x00;

    // 读数据
    uint8_t read_data = I2C1_Read(slave_addr, reg_addr);

    // 处理读取的数据
    // 例如,将数据打印到串口或显示在LED上
    while (1) {
        // 主循环
    }
}

I2C主模式读写操作的注意事项

  1. 起始信号:起始信号必须在SCL线为高电平时生成,SDA线由高变低。
  2. 从设备地址:发送从设备地址时,必须指定读写方向。写操作时使用I2C_Direction_Transmitter,读操作时使用I2C_Direction_Receiver
  3. 寄存器地址:在读写操作中,通常需要先发送寄存器地址,以便从设备知道要读写哪个寄存器。
  4. 应答信号:每个字节传输后,从设备会发送一个应答信号。主设备需要等待应答信号完成后再进行下一步操作。
  5. 重复起始信号:在读操作中,生成重复起始信号是为了在同一通信过程中切换读写方向。
  6. 停止信号:停止信号必须在SCL线为高电平时生成,SDA线由低变高。

I2C主模式的中断处理

中断处理是I2C通信中非常重要的部分,可以提高通信的稳定性和效率。M0S10系列单片机的I2C模块支持多种中断,包括事件中断、错误中断和缓冲区中断。

  • 事件中断:处理I2C通信中的各种事件,如起始信号、地址匹配、数据传输完成等。
  • 错误中断:处理I2C通信中的各种错误,如总线错误、仲裁丢失、应答错误等。
  • 缓冲区中断:处理I2C通信中的数据缓冲区事件,如缓冲区为空或数据准备好。

在中断服务例程中,需要根据不同的中断类型进行相应的处理,确保通信的顺利进行。

I2C主模式的DMA支持

M0S10系列单片机的I2C模块支持DMA(直接内存访问),可以在不占用CPU资源的情况下进行大量数据的传输。使用DMA可以显著提高数据传输效率,特别是在需要传输大量数据的应用中。

配置DMA的基本步骤如下:

  1. 使能DMA时钟:在使用DMA之前,需要使能DMA时钟。
  2. 配置DMA通道:选择合适的DMA通道,并配置传输方向、数据大小、传输模式等。
  3. 配置I2C模块的DMA请求:使能I2C模块的DMA请求,将数据传输任务交给DMA控制器。
  4. 启动DMA传输:在生成起始信号后,启动DMA传输。

以下是一个简单的I2C主模式写操作使用DMA的示例:

#include "stm32f10x.h"

void I2C1_Config(void) {
    // 使能I2C1时钟
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C1, ENABLE);

    // 配置I2C1为主模式
    I2C_DeInit(I2C1);
    I2C_InitTypeDef I2C_InitStructure;
    I2C_InitStructure.I2C_Mode = I2C_Mode_I2C;
    I2C_InitStructure.I2C_DutyCycle = I2C_DutyCycle_2;
    I2C_InitStructure.I2C_OwnAddress1 = 0x00;
    I2C_InitStructure.I2C_Ack = I2C_Ack_Enable;
    I2C_InitStructure.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit;
    I2C_InitStructure.I2C_ClockSpeed = 100000; // 100kHz
    I2C_Init(I2C1, &I2C_InitStructure);

    // 使能I2C1
    I2C_Cmd(I2C1, ENABLE);

    // 使能I2C1中断
    I2C_ITConfig(I2C1, I2C_IT_EVT | I2C_IT_ERR | I2C_IT_BUF, ENABLE);

    // 配置I2C1中断通道
    NVIC_InitTypeDef NVIC_InitStructure;
    NVIC_InitStructure.NVIC_IRQChannel = I2C1_EV_IRQn;
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
    NVIC_Init(&NVIC_InitStructure);

    NVIC_InitStructure.NVIC_IRQChannel = I2C1_ER_IRQn;
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2;
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
    NVIC_Init(&NVIC_InitStructure);
}

void I2C1_DMA_Config(uint8_t* data, uint16_t length) {
    // 使能DMA1时钟
    RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);

    // 配置DMA1通道1
    DMA_InitTypeDef DMA_InitStructure;
    DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)(&I2C1->DR);
    DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)(&data[0]);
    DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST;
    DMA_InitStructure.DMA_BufferSize = length;
    DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
    DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
    DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;
    DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;
    DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;
    DMA_InitStructure.DMA_Priority = DMA_Priority_High;
    DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;
    DMA_Init(DMA1_Channel1, &DMA_InitStructure);

    // 使能DMA1通道1
    DMA_Cmd(DMA1_Channel1, ENABLE);

    // 使能I2C1的DMA请求
    I2C_DMARequestConfig(I2C1, I2C_DMAREquest_Enable);
}

void I2C1_Write_DMA(uint8_t slave_addr, uint8_t reg_addr, uint8_t* data, uint16_t length) {
    // 生成起始信号
    I2C_GenerateSTART(I2C1, ENABLE);

    // 等待起始信号完成
    while (!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_MODE_SELECT)) {}

    // 发送从设备地址
    I2C_Send7bitAddress(I2C1, slave_addr, I2C_Direction_Transmitter);

    // 等待从设备地址被应答
    while (!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED)) {}

    // 发送寄存器地址
    I2C_SendData(I2C1, reg_addr);

    // 等待寄存器地址被应答
    while (!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_TRANSMITTED)) {}

    // 配置DMA
    I2C1_DMA_Config(data, length);

    // 使能DMA传输
    I2C_DMACmd(I2C1, ENABLE);

    // 等待DMA传输完成
    while (DMA_GetFlagStatus(DMA1_FLAG_TC1) == RESET) {}

    // 生成停止信号
    I2C_GenerateSTOP
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值