历史上的今天
返回首页

历史上的今天

今天是:2024年09月26日(星期四)

正在发生

2021年09月26日 | STM8S GPIO模拟I2C的底层代码

2021-09-26 来源:eefocus

底层代码

你好! 这是你第一次使用 Markdown编辑器 所展示的欢迎页。如果你想学习如何使用Markdown编辑器, 可以仔细阅读这篇文章,了解一下Markdown的基本语法知识。


GPIO初始化设置

这颗STM8S上,具有真·开漏功能的引脚是PB4和PB5,这两个引脚也正是片上I2C外设的默认引脚。端口设置如下:


PB4 :I2C的SCL时钟线,开漏输出模式

PB5 :I2C的SDA数据线,开漏输出模式

SCL和SDA初始化时,推荐选用高阻态输出

/**

  * @brief  I2C端口初始化

  * @param  None

  * @retval None

  */

void I2C_GPIO_Init(void)

{

  GPIO_Init(I2C_PORT, I2C_SDA_PIN, GPIO_MODE_OUT_OD_HIZ_SLOW);

  GPIO_Init(I2C_PORT, I2C_SCL_PIN, GPIO_MODE_OUT_OD_HIZ_SLOW);

  I2C_SDA_OUT(1);

  I2C_SCL_OUT(1);

}


I2C主机向从机发送一个字节,都会等待从机通过SDA线返回一个“已接收(ACK)”指令。此时主机的SDA应设置为输入,而在其他环节中应该保持开漏输出。而根据不同从机的I2C时序,有时也需要将SCL设置为输入。

/**

  * @brief  SDA设为输出

  * @param  None

  * @retval None

  */

void I2C_SDA_SET_OUTPUT(void)

{

  GPIO_Init(I2C_PORT, I2C_SDA_PIN, GPIO_MODE_OUT_OD_HIZ_SLOW);

}


/**

  * @brief  SCL设为输出

  * @param  None

  * @retval None

  */

void I2C_SCL_SET_OUTPUT(void)

{

  GPIO_Init(I2C_PORT, I2C_SCL_PIN, GPIO_MODE_OUT_OD_HIZ_SLOW);

}


/**

  * @brief  SDA设为输入

  * @param  None

  * @retval None

  */

void I2C_SDA_SET_INPUT(void)

{

  GPIO_Init(I2C_PORT, I2C_SDA_PIN, GPIO_MODE_IN_FL_NO_IT);

}


/**

  * @brief  SCL设为输入

  * @param  None

  * @retval None

  */

void I2C_SCL_SET_INPUT(void)

{

  GPIO_Init(I2C_PORT, I2C_SCL_PIN, GPIO_MODE_IN_FL_NO_IT);

}


GPIO输入检测

既然有了SDA或SCL的输入设置,自然需要有判断输入值的函数。


/**

  * @brief  SDA输入电平检测

  * @param  None

  * @retval 1=输入为高电平  0=输入为低电平

  */

unsigned char I2C_SDA_IN(void)

{

  return (!!GPIO_ReadInputPin(I2C_PORT, I2C_SDA_PIN));

}


/**

  * @brief  SCL输入电平检测

  * @param  None

  * @retval 1=输入为高电平  0=输入为低电平

  */

unsigned char I2C_SCL_IN(void)

{

  return (!!GPIO_ReadInputPin(I2C_PORT, I2C_SCL_PIN));

}


模拟I2C代码

包括:


起始条件,结束条件

回从机ACK,回从机NACK: 用于主机从从机读1字节(此时主从的身份对调)后,还要向其发送请继续(ACK) 或 请停下(NACK) 信号

等待从机回ACK: 主机向从机发送1字节后,需要等待从机返回接收到(ACK) ,如果接收到的是 接收到(ACK)

从从机读1字节: 完成读1字节后,需要向从机发送请继续(ACK) 或 请停下(NACK) 信号

向从机写1细节: 完成写1字节后,需要等待从机返回接收到(ACK) 信号。

起始条件,结束条件

/**

  * @brief  I2C传输开始

  * @param  None

  * @retval None

  */

void I2C_START(void)

{

  I2C_SDA_SET_OUTPUT();

  

  I2C_SDA_OUT(1);

  I2C_SCL_OUT(1);

  Delay_1us(10);/

  I2C_SDA_OUT(0);

  Delay_1us(10);

  I2C_SCL_OUT(0);

  Delay_1us(10);

}


/**

  * @brief  I2C传输结束

  * @param  None

  * @retval None

  */

void I2C_STOP(void)

{

  I2C_SDA_SET_OUTPUT();

  

  I2C_SDA_OUT(0);

  I2C_SCL_OUT(0);

  Delay_1us(10);//

  I2C_SCL_OUT(1);

  Delay_1us(10);

  I2C_SDA_OUT(1);

  Delay_1us(10);

}


回从机ACK,回从机NACK

/**

  * @brief  MCU回复IC ACK信号

  * @param  None

  * @retval None

  */

void I2C_SEND_ACK(void)

{

  I2C_SDA_SET_OUTPUT();

  

  I2C_SDA_OUT(0);

  Delay_1us(10);//

  I2C_SCL_OUT(1);

  Delay_1us(10);

  I2C_SCL_OUT(0);

  Delay_1us(10);

}


/**

  * @brief  MCU回复NACK信号

  * @param  None

  * @retval None

  */

void I2C_SEND_NACK(void)

{

  I2C_SDA_SET_OUTPUT();

  

  I2C_SDA_OUT(1);

//  Delay_1us(10);///

  I2C_SCL_OUT(1);

  Delay_1us(10);

  I2C_SCL_OUT(0);

  Delay_1us(10);

}


从从机读1字节

/**

  * @brief  I2C MCU接收1字节

  * @param  ACK_CHOICE:  数据读取后主机如何回复,I2C_ACK=回复ACK  I2C_NACK=回复NACK  I2C_JUMP_REPLY=跳过回复

  * @retval read_data:   接收的字节

  */

unsigned char I2C_READ_BYTE(unsigned char ACK_CHOICE)

{

  unsigned char read_data=0;

  

  I2C_SDA_SET_INPUT();

  

//  I2C_SCL_OUT(0);//

//  Delay_1us(10);///

  

  for(unsigned char i=0x80; i!=0; i>>=1)

  {

    I2C_SCL_OUT(1);

    Delay_1us(10);

    if(I2C_SDA_IN() == 1)

      read_data |= i;

    I2C_SCL_OUT(0);

    Delay_1us(10);

  }

  I2C_SDA_SET_OUTPUT();

  I2C_SDA_OUT(1);//?????????????????

  

  if(ACK_CHOICE==I2C_ACK)       I2C_SEND_ACK();

  if(ACK_CHOICE==I2C_NACK)      I2C_SEND_NACK();

  

  return read_data;

}


向从机写1字节

/**

  * @brief  I2C MCU发送1字节

  * @param  send_data:  发送的字节

  * @retval ack回复:    I2C_NACK=异常=检测到NACK  I2C_ACK=正常=检测到ACK

  */

unsigned char I2C_SEND_BYTE(unsigned char send_data)

{

  unsigned char ack=I2C_NACK;   //初始值

  unsigned char time_out=200;   //ACK查询次数

  


  I2C_SDA_SET_OUTPUT();

//  I2C_SCL_SET_OUTPUT();

  

//  I2C_SCL_OUT(0);

//  Delay_1us(10);/

  

  for(unsigned char i=0x80; i!=0; i>>=1)

  {

    if(send_data&i)     I2C_SDA_OUT(1);

    else                I2C_SDA_OUT(0);

    I2C_SCL_OUT(1);

    Delay_1us(10);

    I2C_SCL_OUT(0);

    Delay_1us(10);

  }

  

  //开始ACK查询

  I2C_SDA_SET_INPUT();

  I2C_SCL_OUT(1);

  while(time_out--)

  {

    if(I2C_SDA_IN()==1)

      ack=I2C_NACK;

    else

    {

      ack=I2C_ACK;

      break;

    }

    Delay_1us(1);

  }

  

  I2C_SCL_OUT(0);

  

  I2C_SDA_SET_OUTPUT();

  I2C_SDA_OUT(1);//???????????????????

  

  return ack;

}


其他代码

上述全部代码位于i2c.c 源文件中,而下列代码则全部位于i2c.h 头文件。


#ifndef __I2C_H

#define __I2C_H

/* Includes ------------------------------------------------------------------*/

#include "main.h"


/* Private defines -----------------------------------------------------------*/

#define I2C_PORT                GPIOB

#define I2C_SDA_PIN             GPIO_PIN_5

#define I2C_SCL_PIN             GPIO_PIN_4


#define I2C_ACK                 0 //用于接收:SDA=L=从机有回复  用于发送:回复从机ACK

#define I2C_NACK                1 //用于接收:SDA=H=从机未回复  用于发送:回复从机ACK

#define I2C_JUMP_REPLY          2 //调试用

/* Private macro -------------------------------------------------------------*/

/* Private variables ---------------------------------------------------------*/

/* Private function prototypes -----------------------------------------------*/

void I2C_GPIO_Init(void);

void I2C_SDA_SET_OUTPUT(void);

void I2C_SCL_SET_OUTPUT(void);

void I2C_SDA_SET_INPUT(void);

void I2C_SCL_SET_INPUT(void);


void I2C_SDA_OUT(unsigned char value);

void I2C_SCL_OUT(unsigned char value);

unsigned char I2C_SDA_IN(void);

unsigned char I2C_SCL_IN(void);


void I2C_START(void);

void I2C_STOP(void);


void I2C_SEND_ACK(void);

void I2C_SEND_NACK(void);


unsigned char I2C_READ_BYTE(unsigned char ACK_CHOICE);

unsigned char I2C_SEND_BYTE(unsigned char send_data);


#endif

推荐阅读

史海拾趣

亿佰特(EBYTE)公司的发展小趣事

亿佰特在发展过程中,荣获了多项荣誉和奖项。公司成功入选2022年度四川省“专精特新”中小企业名单,这是对公司在细分市场专业化、企业管理精细化、产品或服务特色化、科技成果新颖化等方面的认可。同时,亿佰特还积极履行社会责任,关注环保和公益事业,展现了企业的良好形象和责任感。

以上五个故事方向涵盖了亿佰特在电子行业中的发展过程中的重要方面,希望能够帮助您了解该公司的成长历程。

浙江东亚电子(DongYa)公司的发展小趣事

随着公司的发展,浙江东亚电子不断拓展其产业布局。除了传统的电力电子元件、分流器等产品外,公司还涉足新能源汽车、充电桩、变频器和储能等领域。特别是在新能源汽车和充电桩领域,公司紧跟国家“新基建”发展战略,推出了一系列具有竞争力的产品,为公司带来了新的增长点。

达晶微(CREATEK)公司的发展小趣事

为了加强研发实力和应用能力,达晶微在上海设立了研发中心和应用中心。这些中心汇聚了来自全球的优秀人才和先进技术,为公司提供了强大的技术支持和创新动力。通过不断研发新产品和拓展新领域,达晶微在电子行业中保持了领先地位。

CINTERION公司的发展小趣事

CINTERION的前身可追溯到西门子于1995年创建的无线模块业务。在成立之初,该公司便专注于工业用机器对机器(M2M)无线通信模块的研发与生产。随着技术的不断进步,CINTERION逐渐在行业内崭露头角,其产品在智能电网、汽车、公用事业等领域得到了广泛应用。1996年,CINTERION成功推出了全球第一款蜂窝模组,这一创新为公司在无线通信模组市场打下了坚实的基础。

ANYSOLAR LTD.公司的发展小趣事

ANYSOLAR始终关注企业的社会责任和可持续发展。在生产过程中,公司严格遵守环保法规,采用先进的环保技术和设备,确保生产过程中的废弃物排放达到国家标准。同时,公司还积极参与社会公益活动,捐赠光伏产品支持贫困地区的能源建设。

此外,ANYSOLAR还致力于推动清洁能源的普及和应用,通过技术研发和市场推广等方式,降低清洁能源的使用成本,提高其在能源结构中的占比。这些举措不仅体现了ANYSOLAR的社会责任感,也为公司的可持续发展奠定了坚实的基础。

以上便是关于ANYSOLAR LTD.的五个发展故事。虽然这些故事是虚构的,但它们基于电子行业的常见趋势和企业发展的普遍规律,旨在展示一个企业在发展过程中可能面临的挑战和机遇。希望这些故事能够为您提供一些启发和参考。

Econais公司的发展小趣事

随着技术的不断成熟和市场的日益扩大,Econais开始将目光投向全球市场。公司积极参加各类国际展会和研讨会,与全球各地的客户和合作伙伴建立联系。同时,Econais还针对不同地区的市场需求,推出定制化的产品和解决方案。这些努力使得Econais在全球范围内的市场份额逐渐扩大。

问答坊 | AI 解惑

VC++ --->> WinCE 的问题(对大家应该不会很难)

在VC中用到了一个类CxImage,编译和链接都没有问题,(*.lib的调用也正常)。但是把程序移植到WinCE中,在链接的时候总是出现这样的错误信息,这是什么原因呢? ximage.obj : error LNK2019: 无法解析的外部符号 \"public: void __cdecl CxImage::S ...…

查看全部问答>

wince audio device 开发求助

前断时间把wince上的usb VIDEO device做好了(是slave的不是host),现在老板又需要做USB AUDIO DEVICE. USB部分已经正常能够发送数据,在电脑上录音设备也能录到声音。但是要真正实现把CE设备的声音通过USB传到电脑上来播放还必须要去修改AUDIO驱 ...…

查看全部问答>

怎么从/PROC/DEVICES中得到动态主设备号?脚本怎么样写,应该写在哪个文件里,怎么样在linux启动时运行这个脚本?谢谢!

怎么从/PROC/DEVICES中得到动态主设备号?脚本怎么样写,应该写在哪个文件里,怎么样在linux启动时运行这个脚本?谢谢!…

查看全部问答>

主机和目标机连接不上什么原因???????????????????????????

初学VXWORKS不太清楚是不是网卡驱动没有加载?ping也ping不通 我用ifShow显示: lo (unit number 0):      Flags: (0x8069) UP LOOPBACK MULTICAST ARP RUNNING      Type: SOFTWARE_LOOPBACK   ...…

查看全部问答>

电子膨胀阀驱动控制器

电子膨胀阀驱动控制器…

查看全部问答>

求助:如何去除交流采样AD出来的声音信号结果中含有直流分量?

交流采样AD出来的声音信号结果中含有直流分量(可以认为是零漂),即交直流混合电平,感觉上好像这个直流分量会发生变化,可以排除是AD前面电路造成的问题,也就是说可以确认是AD输出的漂移造成的,我想是否可以用数字滤波的方法去掉它,如果可以, ...…

查看全部问答>

2418的电流过大问题太烦了。

现在重新测试,同样的板子,程序也一样。 用149一切正常, 换为2418,电流有1.3ma,而且会经常自动重启。。。 郁闷啊。。。。。。。 lierda的工程师们知道为什么吗?…

查看全部问答>

【MSP430共享】430数据采集方案(强烈推荐)

常见的通讯控制电路里面都包含了,一般的430开发项目直接照搬电路原理图就可以了,包含485通讯,电流、电压、频率数据采集,外部控制,数码管显示、红外遥控等等等功能…

查看全部问答>

如何设计FPGA复位电路----------菜鸟一步一步走

复位对于FPGA设计很重要,一些很奇怪而找不到原因的问题很有可能是由于复位造成的,本人就因为没理解好复位情况而受困扰几天。 复位信号可以是高电平也可以是低电平复位。并且电路分外部信号的硬件电路复位和内部信号的软件复位。 外部硬件复 ...…

查看全部问答>