模拟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; //返回读取到的数据
}
————————————————
版权声明:本文为CSDN博主「QAQlfk」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/qq_40307946/article/details/110367264
|
|