STM32F103学习笔记(1)——FreeRTOS下模拟I2C

一、硬件连接

功能口 引脚
SCL PB.6
SDA PB.5

二、移植文件

链接:https://pan.baidu.com/s/1wxbQTMlnX2pavrbW2RYg4g 提取码:dxex
board_i2c.cboard_i2c.h 两个文件加入工程的User文件夹下

注意:延时函数使用了FreeRTOS的vTaskDelay任务延时函数

2.1 board_i2c.c

/*********************************************************************
 * INCLUDES
 */
#include "FreeRTOS.h"
#include "task.h" 
 
#include "board_i2c.h"
#include "board_systick.h"

static void SDA_OUT_MODE(void);
static void SDA_IN_MODE(void);

/*********************************************************************
 * PUBLIC FUNCTIONS
 */
/**
 @brief I2C驱动初始化,采用模拟IO的方式实现
 @param 无
 @return 无
*/
void IIC_Init(void)
{
    GPIO_InitTypeDef GPIO_InitStructure;
    RCC_APB2PeriphClockCmd(IIC_SCL_CLK | IIC_SDA_CLK, ENABLE);
	
    GPIO_InitStructure.GPIO_Pin = IIC_SCL_PIN;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;            
    GPIO_Init(IIC_SCL_PORT, &GPIO_InitStructure);
	
    GPIO_InitStructure.GPIO_Pin = IIC_SDA_PIN;
    GPIO_Init(IIC_SDA_PORT, &GPIO_InitStructure);
	
    IIC_Stop();             // 给一个停止信号, 复位I2C总线上的所有设备到待机模式
}

/**
 @brief CPU发起I2C总线启动信号
 @param 无
 @return 无
*/
void IIC_Start(void)
{
    SDA_OUT_MODE();         // SDA线输出模式
    
    IIC_SDA_1();            	  	  
    IIC_SCL_1();
    vTaskDelay(1);
    IIC_SDA_0();            // 当SCL高电平时,SDA出现一个下跳沿表示I2C总线启动信号
    vTaskDelay(1);    
    IIC_SCL_0();            // 钳住I2C总线,准备发送或接收数据 
}	

/**
 @brief CPU发起I2C总线停止信号
 @param 无
 @return 无
*/
void IIC_Stop(void)
{
    SDA_OUT_MODE();         // SDA线输出模式
    
    IIC_SCL_0();
    IIC_SDA_0();          
    IIC_SCL_1();
    vTaskDelay(1);
    IIC_SDA_1();            // 当SCL高电平时,SDA出现一个上跳沿表示I2C总线停止信号		
    vTaskDelay(1);
}

/**
 @brief CPU向I2C总线设备发送8bit数据
 @param ucByte -[in] 等待发送的字节
 @return 无
*/  
void IIC_SendByte(uint8_t ucByte)
{                        
    uint8_t i;  
    
    SDA_OUT_MODE();         // SDA线输出模式
    
    IIC_SCL_0();            // 拉低时钟开始数据传输
    
    for(i = 0; i < 8; i++)
    {              
        if(ucByte & 0x80) 	
        {            
            IIC_SDA_1();
        }
        else
        {
            IIC_SDA_0();
        }
		ucByte <<= 1; 	
        vTaskDelay(1);        
        IIC_SCL_1();
        vTaskDelay(1);
        IIC_SCL_0();
        vTaskDelay(1);        
    }	 
} 

/**
 @brief CPU从I2C总线设备读取8bit数据
 @param 无
 @return 读到的数据
*/ 
uint8_t IIC_ReadByte(void)
{
    uint8_t i = 0;
    uint8_t value = 0;
    
    SDA_IN_MODE();          // SDA线输入模式
    
    for(i = 0; i < 8; i++)
    {
        value <<= 1;
        IIC_SCL_1();
        vTaskDelay(1);
        if(IIC_SDA_READ())
        {
            value++;
        }            
        IIC_SCL_0(); 
        vTaskDelay(1); 
    }					
    IIC_Ack();
    
    return value;
}

/**
 @brief CPU产生一个时钟,并读取器件的ACK应答信号
 @param 无
 @return 返回0表示正确应答,1表示无器件响应
*/
uint8_t IIC_WaitAck(void)
{
    uint8_t result = 0; 
    
    SDA_IN_MODE();          // SDA线输入模式
    
    IIC_SDA_1();            // CPU释放SDA总线
    vTaskDelay(1); 
    IIC_SCL_1();            // CPU驱动SCL = 1, 此时器件会返回ACK应答
    vTaskDelay(1);
    if(IIC_SDA_READ())
    {
        result = 1;
    }
    else
    {
        result = 0;
    }
    IIC_SCL_0();
    vTaskDelay(1);

    return result;  
} 

/**
 @brief CPU产生一个ACK信号
 @param 无
 @return 无
*/
void IIC_Ack(void)
{
    SDA_OUT_MODE();         // SDA线输出模式
    
    IIC_SDA_0();            // CPU驱动SDA = 0
    vTaskDelay(1);
    IIC_SCL_1();            // CPU产生1个时钟
    vTaskDelay(1);
    IIC_SCL_0();
    vTaskDelay(1);
    IIC_SDA_1();            // CPU释放SDA总线
}

/**
 @brief CPU产生1个NACK信号
 @param 无
 @return 无
*/    
void IIC_NAck(void)
{
    SDA_OUT_MODE();         // SDA线输出模式
    
    IIC_SDA_1();            // CPU驱动SDA = 1
    vTaskDelay(1);
    IIC_SCL_1();            // CPU产生1个时钟
    vTaskDelay(1);
    IIC_SCL_0();
    vTaskDelay(1);
}

/**
 @brief 检测I2C总线设备,CPU向发送设备地址,然后读取设备应答来判断该设备是否存在
 @param address -[in] 设备的I2C总线地址+读写控制bit(0 = w, 1 = r)
 @return 0 - 表示正确, 1 - 表示未探测到
*/  
uint8_t IIC_CheckDevice(uint8_t address)
{
    uint8_t ucAck;

    IIC_Init();             // 初始化I2C
    IIC_Start();            // 发送启动信号
    IIC_SendByte(address);  // 设备的I2C总线地址+读写控制bit(0 = w, 1 = r)
    ucAck = IIC_WaitAck();	// 检测设备的ACK应答
    IIC_Stop();             // 发送停止信号

    return ucAck;
}


/*********************************************************************
 * LOCAL FUNCTIONS
 */
/**
 @brief SDA输出配置	
 @param 无
 @return 无
*/ 
static void SDA_OUT_MODE(void)
{
    GPIO_InitTypeDef GPIO_InitStructure;
	
    GPIO_InitStructure.GPIO_Pin = IIC_SDA_PIN;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
    GPIO_Init(IIC_SDA_PORT, &GPIO_InitStructure);
}

/**
 @brief SDA输入配置	
 @param 无
 @return 无
*/ 
static void SDA_IN_MODE(void)
{
    GPIO_InitTypeDef GPIO_InitStructure;
	
    GPIO_InitStructure.GPIO_Pin = IIC_SDA_PIN;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
    GPIO_Init(IIC_SDA_PORT, &GPIO_InitStructure);
}

/****************************************************END OF FILE****************************************************/

2.2 board_i2c.h

#ifndef _BOARD_I2C_H_
#define _BOARD_I2C_H_

/*********************************************************************
 * INCLUDES
 */
#include "stm32f10x.h"

/*********************************************************************
 * DEFINITIONS
 */
// I2C_SCL时钟
#define IIC_SCL_CLK         RCC_APB2Periph_GPIOB        // GPIO端口时钟
#define IIC_SCL_PORT        GPIOB                       // GPIO端口
#define IIC_SCL_PIN         GPIO_Pin_6                  // GPIO引脚
// I2C_SDA时钟
#define IIC_SDA_CLK         RCC_APB2Periph_GPIOB        // GPIO端口时钟
#define IIC_SDA_PORT        GPIOB                       // GPIO端口
#define IIC_SDA_PIN         GPIO_Pin_5                  // GPIO引脚

/*********************************************************************
 * MACROS
 */
#define IIC_SCL_0()         GPIO_ResetBits(IIC_SCL_PORT, IIC_SCL_PIN) 
#define IIC_SCL_1()         GPIO_SetBits(IIC_SCL_PORT, IIC_SCL_PIN)
#define IIC_SDA_0()         GPIO_ResetBits(IIC_SDA_PORT, IIC_SDA_PIN) 
#define IIC_SDA_1()         GPIO_SetBits(IIC_SDA_PORT, IIC_SDA_PIN) 
#define IIC_SDA_READ()      GPIO_ReadInputDataBit(IIC_SDA_PORT, IIC_SDA_PIN) 

/*********************************************************************
 * API FUNCTIONS
 */
void IIC_Init(void);			 
void IIC_Start(void);
void IIC_Stop(void);
void IIC_SendByte(uint8_t ucByte);
uint8_t IIC_ReadByte(void);
uint8_t IIC_WaitAck(void);
void IIC_Ack(void);
void IIC_NAck(void);
uint8_t IIC_CheckDevice(uint8_t address);

#endif /* _BOARD_I2C_H_ */

三、API调用

需包含头文件 board_i2c.h

IIC_Init

功能 I2C驱动初始化,采用模拟IO的方式实现
函数定义 void IIC_Init(void)
参数
返回

IIC_Start

功能 CPU发起I2C总线启动信号
函数定义 void IIC_Start(void)
参数
返回

IIC_Stop

功能 CPU发起I2C总线停止信号
函数定义 void IIC_Stop(void)
参数
返回

IIC_SendByte

功能 CPU向I2C总线设备发送8bit数据
函数定义 void IIC_SendByte(uint8_t ucByte)
参数 ucByte:等待发送的字节
返回

IIC_ReadByte

功能 CPU从I2C总线设备读取8bit数据
函数定义 uint8_t IIC_ReadByte(void)
参数
返回 读到的数据

IIC_WaitAck

功能 CPU产生一个时钟,并读取器件的ACK应答信号
函数定义 uint8_t IIC_WaitAck(void)
参数
返回 0表示正确应答,1表示无器件响应

IIC_Ack

功能 CPU产生一个ACK信号
函数定义 void IIC_Ack(void)
参数
返回

IIC_NAck

功能 CPU产生1个NACK信号
函数定义 void IIC_NAck(void)
参数
返回

IIC_CheckDevice

功能 检测I2C总线设备,CPU向发送设备地址,然后读取设备应答来判断该设备是否存在
函数定义 uint8_t IIC_CheckDevice(uint8_t address)
参数 address:设备的I2C总线地址+读写控制bit(0 = w, 1 = r)
返回 0 - 表示正确, 1 - 表示未探测到

四、使用例子

1)添加头文件

#include "board_i2c.h"

2)添加初始化代码

int main(void)
{	
    BaseType_t xReturn = pdPASS;                                        // 定义一个创建信息返回值,默认为pdPASS
  
    /*-------------------------- 外设驱初始化 ---------------------------*/
    NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);                     // STM32中断优先级分组为4,即4bit都用来表示抢占优先级,范围为:0~15优先级分组只需要分组一次即可,以后如果有其他的任务需要用到中断,都统一用这个优先级分组,千万不要再分组,切忌
    Board_LedInit();                                                    // LED灯驱动初始化
    Board_KeyInit();                                                    // 按键驱动初始化 
    USART_Config();                                                     // 串口驱动初始化    
  
    printf("-------------- I2C Init--------------\r\n");
    // 这里写入设备的I2C总线地址
    uint8_t i2cDevice = IIC_CheckDevice(TEA5767_ADDR_W);            // 初始化I2C,并检测是否有设备
    if(!i2cDevice)
    {
        printf("---- I2C OK ----\r\n");  
    }   
    else
    {
        printf("---- I2C ERROR ----\r\n");  
    }  
  
    /*-------------------------- 任务创建 ---------------------------*/
    xReturn = xTaskCreate((TaskFunction_t)appTaskCreate,                // 任务入口函数
                          (const char*   )"appTaskCreate",              // 任务名字
                          (uint16_t      )512,                          // 任务栈大小
                          (void*         )NULL,                         // 任务入口函数参数
                          (UBaseType_t   )1,                            // 任务的优先级
                          (TaskHandle_t* )&s_appTaskCreateHandle);      // 任务控制块指针
                          
    /*-------------------------- 开启调度 ---------------------------*/           
    if(pdPASS == xReturn)
    {
        vTaskStartScheduler();                                      
    }
    else
    {
        return -1;
    }
  
    while(1)
    {
        /* 正常不会执行到这里 */
    };   
}

• 由 Leung 写于 2020 年 6 月 15 日

• 参考:【STM32】IIC的基本原理(实例:普通IO口模拟IIC时序读取24C02)

猜你喜欢

转载自blog.csdn.net/qq_36347513/article/details/106763827