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)

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 215,294评论 6 497
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,780评论 3 391
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 161,001评论 0 351
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,593评论 1 289
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,687评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,679评论 1 294
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,667评论 3 415
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,426评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,872评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,180评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,346评论 1 345
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,019评论 5 340
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,658评论 3 323
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,268评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,495评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,275评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,207评论 2 352