stm32之软件模拟IIC

        在之前的文章中分析过在52上的IIC时序,也测试过stm32的自带IIC功能,这里大致写下如何模拟stm32上的IIC。实验硬件基于stm32f103c8t6

废话不多说,先直接上代码。

一、源码

头文件


#include "stdint.h"
#include "gpio.h"

#ifndef __IIC_H__
#define	__IIC_H__


/*********************引脚和主频根据情况更换*****************************/
#define CPU_FREQUENCY_MHZ  72		 			 // STM32时钟主频

#define IIC_GPIO_PORT	GPIOB			    	 // GPIO端口 
#define IIC_SCL_PIN		GPIO_PIN_9			 // 连接到SCL时钟线的GPIO引脚
#define IIC_SDA_PIN		GPIO_PIN_8			 // 连接到SDA数据线的GPIO引脚
 
#define IIC_SCL_1()  IIC_GPIO_PORT->BSRR = IIC_SCL_PIN											// SCL = 1 
#define IIC_SCL_0()  IIC_GPIO_PORT->BSRR = (uint32_t)IIC_SCL_PIN << 16U  		// SCL = 0 
 
#define IIC_SDA_1()  IIC_GPIO_PORT->BSRR = IIC_SDA_PIN   										// SDA = 1 
#define IIC_SDA_0()  IIC_GPIO_PORT->BSRR = (uint32_t)IIC_SDA_PIN << 16U  		// SDA = 0 
 
#define IIC_SDA_READ()  (IIC_GPIO_PORT->IDR & IIC_SDA_PIN)									// 读SDA引脚 
#define IIC_SCL_READ()  (IIC_GPIO_PORT->IDR & IIC_SCL_PIN)									// 读SCL引脚
 
 
void iic_start(void); //开始信号

void iic_stop(void); //停止信号

void iic_send_byte(uint8_t _ucByte); //发送一个字节

void iic_ack(void); //应答信号

uint8_t iic_read_byte(void); //读取一个字节

uint8_t iic_wait_ack(void); //等待应答信号


#endif

.m文件

#include "iic.h"
 
 void delay_us(__IO uint32_t delay) {
    int last, curr, val;
    int temp;

    while (delay != 0) { 
        temp = delay > 900 ? 900 : delay;
        last = SysTick->VAL;
        curr = last - CPU_FREQUENCY_MHZ * temp;
        if (curr >= 0){
            do{
                val = SysTick->VAL;
            }
            while ((val < last) && (val >= curr));
        } else{
            curr += CPU_FREQUENCY_MHZ * 1000;
            do{
                val = SysTick->VAL;
            }
            while ((val <= last) || (val > curr));
        }
        delay -= temp;
    }
}
 
// IIC 至少0.6us,1us即可
void iic_delay_1us(void) {
	delay_us(1);
}

// SCL保持高电平期间,SDA由高电平变成低电平,产生一个下降沿。
void iic_start(void) {
	IIC_SDA_1();
	IIC_SCL_1();
	iic_delay_1us();
	IIC_SDA_0();
	iic_delay_1us();
}
  
// SCL保持高电平期间,SDA由低电平变成高电平,产生一个上升沿。
void iic_stop(void){
	IIC_SDA_0();
	IIC_SCL_1();
	iic_delay_1us();
	IIC_SDA_1();
	iic_delay_1us();
}

void iic_send_byte(uint8_t _ucByte){
	uint8_t i;
	for (i = 0; i < 8; i++) {
		IIC_SCL_0();
		if (_ucByte & 0x80){
			IIC_SDA_1();
		} else{
			IIC_SDA_0();
		}
		iic_delay_1us();
		IIC_SCL_1();
		iic_delay_1us();
		IIC_SCL_0();
		// 发送最后一位
		if (i == 7) {
			 IIC_SDA_1(); 	// 释放总线
		}
		_ucByte <<= 1;		// 左移一个bit 
		iic_delay_1us();
	}
}

uint8_t iic_read_byte(void){
	uint8_t i;
	uint8_t value = 0;
	for (i = 0; i < 8; i++) {
		value <<= 1;
		IIC_SCL_1();
		iic_delay_1us();
		if (IIC_SDA_READ()) {
			value++;
		}
		IIC_SCL_0();
		iic_delay_1us();
	}
	return value;
}

uint8_t iic_wait_ack(void){
	uint8_t result;

	IIC_SCL_1();	
	iic_delay_1us();
	result = IIC_SDA_READ() ? 1 :0;
	IIC_SCL_0();
	iic_delay_1us();
	return result;
}

void iic_ack(void){
	IIC_SDA_0();	
	iic_delay_1us();
	IIC_SCL_1();	
	iic_delay_1us();
	IIC_SCL_0();
	iic_delay_1us();
	IIC_SDA_1();	
}
 
 
uint8_t iic_CheckDevice(uint8_t _Address){
	uint8_t ucAck;
 
	if (IIC_SDA_READ() && IIC_SCL_READ()){
		iic_start();		/* 发送启动信号 */
 
		/* 发送设备地址+读写控制bit(0 = w, 1 = r) bit7 先传 */
		iic_send_byte(_Address | 0);
		ucAck = iic_wait_ack();	
 
		iic_stop();			
 
		return ucAck;
	}
	return 1;	
}
 
 
void Device_WriteData(uint8_t DeciveAddr,uint8_t *Data,int size){
    int count=size;
		uint8_t *pData=Data;
		//起始信号
    iic_start();    
    //发送器件地址                         
    iic_send_byte(DeciveAddr);       
    //等待应答
    iic_wait_ack();                          
    while(count--)
		{
			//发送数据
			iic_send_byte(*pData++);                     
			//等待应答
			iic_wait_ack();   
		}			 
                       
    //结束信号
    iic_stop();     
}



二、CubeMX配置

stm32之软件模拟IIC_第1张图片

 实验用了PB8和PB9,设置的都是开漏输出式,因为在这个模式下有如下说明:

stm32之软件模拟IIC_第2张图片

在配置成开漏输出时,可以读IDR寄存器。 

三、源码分析

整个源码的格式写法和之前手撸51中的IIC的类似,包括start,stop, ack等的时序。

这里是直接操作的寄存器来输出/输入数据。输出时比如SDA,设置BSRR,读取状态时,比如SDA中的ack,直接读取IDR寄存器。

#define IIC_SCL_1()  IIC_GPIO_PORT->BSRR = IIC_SCL_PIN											// SCL = 1 
#define IIC_SCL_0()  IIC_GPIO_PORT->BSRR = (uint32_t)IIC_SCL_PIN << 16U  		// SCL = 0 
 
#define IIC_SDA_1()  IIC_GPIO_PORT->BSRR = IIC_SDA_PIN   										// SDA = 1 
#define IIC_SDA_0()  IIC_GPIO_PORT->BSRR = (uint32_t)IIC_SDA_PIN << 16U  		// SDA = 0 
 
#define IIC_SDA_READ()  (IIC_GPIO_PORT->IDR & IIC_SDA_PIN)									// 读SDA引脚 
#define IIC_SCL_READ()  (IIC_GPIO_PORT->IDR & IIC_SCL_PIN)									// 读SCL引脚

其它地方就没什么分析的了,和51的代码都一致,O(∩_∩)O哈哈~

完整代码

你可能感兴趣的:(stm32,嵌入式硬件,单片机)