SWM260模拟IIC

本文档描述了在SWM260微控制器上移植验证过的IIC程序遇到的问题及解决过程。作者发现初始化GPIO时,由于if...else...语句的结构,导致只执行了上拉而未执行开漏输出,从而影响了模拟IIC的正常工作。通过调整GPIO初始化函数,确保同时设置开漏输出和上拉,最终解决了问题。同时,提供了完整的模拟IIC代码供参考。
摘要由CSDN通过智能技术生成

模拟IIC的程序代码到处都是,我用的一个在ST的板子上已经验证过的IIC程序移植到SWM260上;

结果,怎么都出不来想要的结果,示波器看波形也是对的,百思不得其解,查了好久好久,最后找到了华芯微特官方给的GPIO初始化操作的的函数上了;

SWM260的GPIO初始化函数如下:

/****************************************************************************************************************************************** 
* 函数名称: GPIO_Init()
* 功能说明:	引脚初始化,包含引脚方向、上拉、下拉、开漏
* 输    入: GPIO_TypeDef * GPIOx	    指定GPIO端口,有效值包括GPIOA、GPIOB、GPIOC、GPIOD
*			uint32_t n		       指定GPIO引脚,有效值包括PIN0、PIN1、PIN2、... ... PIN14、PIN15
*			uint32_t dir	       引脚方向,0 输入        1 输出
*			uint32_t pull_up	   上拉使能
*			uint32_t pull_down	   下拉使能
*			uint32_t open_drain	   开漏使能
* 输    出: 无
* 注意事项: 无
******************************************************************************************************************************************/
void GPIO_Init(GPIO_TypeDef * GPIOx, uint32_t n, uint32_t dir, uint32_t pull_up, uint32_t pull_down, uint32_t open_drain)
{
	switch((uint32_t)GPIOx)
	{
	case ((uint32_t)GPIOA):
		SYS->CLKEN0 |= (0x01 << SYS_CLKEN0_GPIOA_Pos);
		
		PORT_Init(PORTA, n, 0, 1);			//PORTA.PINn引脚配置为GPIO功能,数字输入开启
		if(dir == 1)
		{			
			GPIOA->DIR |= (0x01 << n);
		}
		else
		{
			GPIOA->DIR &= ~(0x01 << n);
		}
		
		if(pull_up == 1)     PORT->PORTA_PULLU |=  (1 << n);
		else                 PORT->PORTA_PULLU &= ~(1 << n);
		if(pull_down == 1)   PORT->PORTA_PULLD |=  (1 << n);
		else                 PORT->PORTA_PULLD &= ~(1 << n);
		if(open_drain == 1)  PORT->PORTA_OPEND |=  (1 << n);
		else                 PORT->PORTA_OPEND &= ~(1 << n);
		break;
	
	case ((uint32_t)GPIOB):
		SYS->CLKEN0 |= (0x01 << SYS_CLKEN0_GPIOB_Pos);
		
		PORT_Init(PORTB, n, 0, 1);
		if(dir == 1)
		{			
			GPIOB->DIR |=  (0x01 << n);
		}
		else
		{
			GPIOB->DIR &= ~(0x01 << n);
		}
		
		if(pull_up == 1)     PORT->PORTB_PULLU |=  (1 << n);
		else                 PORT->PORTB_PULLU &= ~(1 << n);
		if(pull_down == 1)   PORT->PORTB_PULLD |=  (1 << n);
		else                 PORT->PORTB_PULLD &= ~(1 << n);
		if(open_drain == 1)  PORT->PORTB_OPEND |=  (1 << n);
		else                 PORT->PORTB_OPEND &= ~(1 << n);
		break;
	
	case ((uint32_t)GPIOC):
		SYS->CLKEN0 |= (0x01 << SYS_CLKEN0_GPIOC_Pos);
		
		PORT_Init(PORTC, n, 0, 1);
		if(dir == 1)
		{			
			GPIOC->DIR |=  (0x01 << n);
		}
		else
		{
			GPIOC->DIR &= ~(0x01 << n);
		}
		
		if(pull_up == 1)     PORT->PORTC_PULLU |=  (1 << n);
		else                 PORT->PORTC_PULLU &= ~(1 << n);
		if(pull_down == 1)   PORT->PORTC_PULLD |=  (1 << n);
		else                 PORT->PORTC_PULLD &= ~(1 << n);
		if(open_drain == 1)  PORT->PORTC_OPEND |=  (1 << n);
		else                 PORT->PORTC_OPEND &= ~(1 << n);
		break;
	
	case ((uint32_t)GPIOD):
		SYS->CLKEN0 |= (0x01 << SYS_CLKEN0_GPIOD_Pos);
		
		PORT_Init(PORTD, n, 0, 1);
		if(dir == 1)
		{			
			GPIOD->DIR |=  (0x01 << n);
		}
		else
		{
			GPIOD->DIR &= ~(0x01 << n);
		}
		
		if(pull_up == 1)     PORT->PORTD_PULLU |=  (1 << n);
		else                 PORT->PORTD_PULLU &= ~(1 << n);
		if(pull_down == 1)   PORT->PORTD_PULLD |=  (1 << n);
		else                 PORT->PORTD_PULLD &= ~(1 << n);
		if(open_drain == 1)  PORT->PORTD_OPEND |=  (1 << n);
		else                 PORT->PORTD_OPEND &= ~(1 << n);
		break;
	}
}

 

都是对寄存器的操作封装,问题部分如下:

IIC初始化时IO需要设置为开漏输出,习惯上还要设置上拉,但该初始化语句采用的else if会导致初始化时只执行上拉,不执行开漏输出,导致模拟IIC失败;

附SWM260的模拟IIC代码,测试可用

/******************************************************************************
* @ File name --> iic.h
* @ Brief     --> MCU模拟IIC通讯函数
* @           --> 要改变传输频率,请修改延时函数中的数值即可
******************************************************************************/
 
#ifndef _iic_h_ 
#define _iic_h_
 
/******************************************************************************
                                 外部函数头文件                        
******************************************************************************/
#include "SWM260.h"
#include "SWM260_gpio.h" 
#include	"SWM260_port.h"

/******************************************************************************
                                 外部引脚修改区                        
******************************************************************************/


#if 0
/*  IIC_SCL时钟端口、引脚定义 */
#define IIC_SCL_PORT 			GPIOB   
#define IIC_SCL_PIN 			PIN11
/*  IIC_SDA时钟端口、引脚定义 */
#define IIC_SDA_PORT 			GPIOB  
#define IIC_SDA_PIN 			PIN10
#else
/*  IIC_SCL时钟端口、引脚定义 */
#define IIC_SCL_PORT 			GPIOA   
#define IIC_SCL_PIN 			PIN10
/*  IIC_SDA时钟端口、引脚定义 */
#define IIC_SDA_PORT 			GPIOA
#define IIC_SDA_PIN 			PIN11

#endif



#define IIC_SCL_High 		GPIO_SetBit(IIC_SCL_PORT,IIC_SCL_PIN)// 输出高电平
#define IIC_SCL_Low 		GPIO_ClrBit(IIC_SCL_PORT,IIC_SCL_PIN)// 输出低电平

#define IIC_SDA_High 		GPIO_SetBit(IIC_SDA_PORT,IIC_SDA_PIN)// 输出高电平
#define IIC_SDA_Low 		GPIO_ClrBit(IIC_SDA_PORT,IIC_SDA_PIN)// 输出低电平 
#define IIC_SDA_Read		GPIO_GetBit(IIC_SDA_PORT,IIC_SDA_PIN)


 
/******************************************************************************
                             对于低速晶振的支持
                     是否使用延时函数进行调整通讯频率
******************************************************************************/
 
#define _USER_DELAY_CLK					0	//定义了则使用延时调整通讯频率
											//0:不使用延时函数调整通讯频率,对于低速MCU时候用
											//1:使用延时函数调整通讯频率,对于高速MCU时候用
 
/******************************************************************************
                                位带操作
******************************************************************************/
//#define IIC_SCL					PBout(6)
//#define IIC_SDA					PBout(7)	//IIC发送数据用
//#define IN_SDA					PBin(7)	    //IIC读取数据用
 
 
/******************************************************************************
                               通讯频率延时函数
                    需要调整通讯频率的请修改此函数值即可
******************************************************************************/
 
#if	_USER_DELAY_CLK==1	//定义了则使用
 
	#define IIC_Delay()                 delay_us(2) //要改变请修改delay_us()中的数值即可
 
#endif
 
/******************************************************************************
                                 外部功能函数
******************************************************************************/
 
void IIC_GPIO_Init(void);	    //GPIO初始化
 
void IIC_Start(void);	        //IIC启动
 
void IIC_Stop(void);	        //IIC停止
 
void IIC_Ack(uint8_t a);	            //主机向从机发送应答信号
 
uint8_t IIC_Write_Byte(uint8_t dat);	    //向IIC总线发送一个字节数据
 
uint8_t IIC_Read_Byte(void);	        //从IIC总线上读取一个字节数据

 
 
#endif  /* end iic.h */

 


#include "iic.h"

void delay_us(uint16_t ms)
{
	uint16_t i;
	while(ms--)
		for(i=0;i<6;i++)	__NOP();	//等待
}
/******************************************************************************
* Function Name --> IIC_GPIO_Init
* Description   --> GPIO初始化
* Input         --> none
* Output        --> none
* Reaturn       --> none 

GPIO_InitStruct.Pin = OUT2_Pin;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_PULLDOWN;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
HAL_GPIO_Init(OUT2_GPIO_Port, &GPIO_InitStruct);
******************************************************************************/
void IIC_GPIO_Init(void)
{
//	PORT_Init(PORTB, IIC_SCL_PIN, PORTB_PIN11_GPIO, 1);
//	PORT_Init(PORTB, IIC_SDA_PIN, PORTB_PIN10_GPIO, 1);		
//	
	GPIO_Init(IIC_SCL_PORT, IIC_SCL_PIN, 1, 0, 0, 1);	//PC12接LED		引脚方向,0输入,1输出;上拉使能;下拉使能;开漏使能
	GPIO_Init(IIC_SDA_PORT, IIC_SDA_PIN, 1, 0, 0, 1);	//PC12接LED		引脚方向,0输入,1输出;上拉使能;下拉使能;开漏使能
	
	
//	PORT_Init(PORTB, PIN11, PORTB_PIN11_I2C4_SCL, 1);	//GPIOB.11配置为I2C4 SCL引脚
//	PORT->PORTB_PULLU |= (1 << PIN11);					//必须使能上拉,用于模拟开漏
//	PORT_Init(PORTB, PIN10, PORTB_PIN10_I2C4_SDA, 1);	//GPIOB.10配置为I2C4 SDA引脚
//	PORT->PORTB_PULLU |= (1 << PIN10);					//必须使能上拉,用于模拟开漏	
	
	IIC_SDA_High;  //置IIC总线空闲
	IIC_SCL_High;
}
 
/******************************************************************************
* Function Name --> SDA_OUT
* Description   --> SDA输出配置	 
******************************************************************************/
void IIC_SDA_OUT(void)
{

	GPIO_Init(IIC_SDA_PORT, IIC_SDA_PIN, 1, 0, 0, 1);	//PC12接LED		引脚方向,0输入,1输出;上拉使能;下拉使能;开漏使能
	
//		SYS->CLKEN0 |= (0x01 << SYS_CLKEN0_GPIOB_Pos);
//		IIC_SDA_PORT->DIR |=  0x400;				//输出
//	GPIOB->DIR |=  (0x01 << IIC_SDA_PIN);				//输出
//  GPIOB->DIR &= ~(0x01 << IIC_SDA_PIN);			//输入
}
 
/******************************************************************************
* Function Name --> SDA_IN
* Description   --> SDA输入配置
******************************************************************************/
void IIC_SDA_IN(void)
{
//	GPIOB->DIR &= ~(0x01 << IIC_SDA_PIN);			//输入
	GPIO_Init(IIC_SDA_PORT, IIC_SDA_PIN, 0, 0, 0, 1);	//PC12接LED		引脚方向,0输入,1输出;上拉使能;下拉使能;开漏使能
}
 
/******************************************************************************
* Function Name --> IIC启动
* Description   --> SCL高电平期间,SDA由高电平突变到低电平时启动总线
*                   SCL: __________
*                                  \__________
*                   SDA: _____
*                             \_______________
******************************************************************************/
void IIC_Start(void)
{
	IIC_SDA_OUT();	//设置成输出
 
	IIC_SDA_High;	//为SDA下降启动做准备
	IIC_SCL_High;	//在SCL高电平时,SDA为下降沿时候总线启动
	
#if _USER_DELAY_CLK==1  /* 定义了则使用延时函数来改变通讯频率 */
	
	IIC_Delay();
	IIC_SDA_Low;	//突变,总线启动
	IIC_Delay();
	IIC_SCL_Low;
	IIC_Delay();
 
#else  /* 否则不使用延时函数改变通讯频率 */
	
	IIC_SDA_Low;	//突变,总线启动
	IIC_SCL_Low;
 
#endif  /* end __USER_DELAY_CLK */
}
 
/******************************************************************************
* Function Name --> IIC停止
* Description   --> SCL高电平期间,SDA由低电平突变到高电平时停止总线
*                   SCL: ____________________
*                                  __________
*                   SDA: _________/
* Input         --> none
* Output        --> none
* Reaturn       --> none 
******************************************************************************/
void IIC_Stop(void)
{
	IIC_SDA_OUT();	//设置成输出
 
	IIC_SDA_Low;	//为SDA上升做准备
	
#if _USER_DELAY_CLK==1  /* 定义了则使用延时函数来改变通讯频率 */
 
	IIC_Delay();
	IIC_SCL_High;	//在SCL高电平时,SDA为上升沿时候总线停止
	IIC_Delay();
	IIC_SDA_High;	//突变,总线停止
	IIC_Delay();
 
#else  /* 否则不使用延时函数改变通讯频率 */
 
	IIC_SCL_High;	//在SCL高电平时,SDA为上升沿时候总线停止
	IIC_SDA_High;	//突变,总线停止
 
#endif  /* end __USER_DELAY_CLK */
}
 
/******************************************************************************
* Function Name --> 主机向从机发送应答信号
* Description   --> none
* Input         --> a:应答信号
*                      0:应答信号
*                      1:非应答信号
* Output        --> none
* Reaturn       --> none 
******************************************************************************/
void IIC_Ack(uint8_t a)
{
	IIC_SDA_OUT();	//设置成输出
 
	if(a)	IIC_SDA_High;	//放上应答信号电平
	else	IIC_SDA_Low;
	
#if _USER_DELAY_CLK==1  /* 定义了则使用延时函数来改变通讯频率 */
 
	IIC_Delay();
	IIC_SCL_High;	//为SCL下降做准备
	IIC_Delay();
	IIC_SCL_Low;	//突变,将应答信号发送过去
	IIC_Delay();
 
#else  /* 否则不使用延时函数改变通讯频率 */
 
	IIC_SCL_High;	//为SCL下降做准备
	IIC_SCL_Low;	//突变,将应答信号发送过去
 
#endif  /* end __USER_DELAY_CLK */
}
 
/******************************************************************************
* Function Name --> 向IIC总线发送一个字节数据
* Description   --> none
* Input         --> dat:要发送的数据
* Output        --> none
* Reaturn       --> ack:返回应答信号
******************************************************************************/
uint8_t IIC_Write_Byte(uint8_t dat)
{
	uint8_t i;
	uint8_t iic_ack=0;	//iic应答标志
 
	IIC_SDA_OUT();	//设置成输出
 
	for(i = 0;i < 8;i++)
	{
		if(dat & 0x80)	IIC_SDA_High;	//判断发送位,先发送高位
		else	IIC_SDA_Low;
 
#if _USER_DELAY_CLK==1  /* 定义了则使用延时函数来改变通讯频率 */
			
		IIC_Delay();
		IIC_SCL_High;	//为SCL下降做准备
		IIC_Delay();
		IIC_SCL_Low;	//突变,将数据位发送过去
		dat<<=1;	//数据左移一位
	}	//字节发送完成,开始接收应答信号
 
	IIC_SDA_High;	//释放数据线
 
	IIC_SDA_IN();	//设置成输入
 
	IIC_Delay();
	IIC_SCL_High;	//为SCL下降做准备
	IIC_Delay();
 
#else  /* 否则不使用延时函数改变通讯频率 */
 
		IIC_SCL_High;	//为SCL下降做准备
		IIC_SCL_Low;	//突变,将数据位发送过去
		dat<<=1;	//数据左移一位
	}	//字节发送完成,开始接收应答信号
 
	IIC_SDA_High;	//释放数据线
 
	IIC_SDA_IN();	//设置成输入
 
	IIC_SCL_High;	//为SCL下降做准备
 
#endif  /* end __USER_DELAY_CLK */
	
	iic_ack |= IIC_SDA_Read;	//读入应答位
	IIC_SCL_Low;
	return iic_ack;	//返回应答信号
}
 
/******************************************************************************
* Function Name --> 从IIC总线上读取一个字节数据
* Description   --> none
* Input         --> none
* Output        --> none
* Reaturn       --> x:读取到的数据
******************************************************************************/
uint8_t IIC_Read_Byte(void)
{
	uint8_t i;
	uint8_t x=0;
 
	IIC_SDA_High;	//首先置数据线为高电平
 
	IIC_SDA_IN();	//设置成输入
 
	for(i = 0;i < 8;i++)
	{
		x <<= 1;	//读入数据,高位在前
 
#if _USER_DELAY_CLK==1  /* 定义了则使用延时函数来改变通讯频率 */
 
		IIC_Delay();
		IIC_SCL_High;	//突变
		IIC_Delay();
		
		if(IIC_SDA_Read)	x |= 0x01;	//收到高电平
 
		IIC_SCL_Low;
		IIC_Delay();
	}	//数据接收完成
 
#else  /* 否则不使用延时函数改变通讯频率 */
 
		IIC_SCL_High;	//突变
		
		if(IIC_SDA_Read)	x |= 0x01;	//收到高电平
 
		IIC_SCL_Low;
	}	//数据接收完成
 
#endif  /* end __USER_DELAY_CLK */
 
	IIC_SCL_Low;
 
	return x;	//返回读取到的数据
}

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值