历史上的今天
返回首页

历史上的今天

今天是:2024年11月28日(星期四)

正在发生

2019年11月28日 | STM8硬件IIC从机

2019-11-28 来源:eefocus

一.平台


芯片:STM8S103F3P6

环境:IAR + STVP

系统:WIN7

在这里插入图片描述
在这里插入图片描述

二. 目的


STM8S103F3P6:使用STM8标准库开发

角色------从机

方式----------硬件IIC


STM32H7:

角色------主机

方式----------IO口模拟IIC主机


主机发送命令包,从机接收后进行判断

主机发送读取命令,从机返回上次命令包判断后要返回的数据包


三.STM8硬件IIC


STM8S103时钟


由于该芯片实际应用是放到控制板作为一个附属芯片,不考虑功耗、尽可能采用高的频率,且需要满足硬件I2C对时钟的需求(后续讲到),由STM8S103参考手册时钟章节可看出,接外部高速时钟最高是16M,内部RC最高16M,因此时钟最高是16M。测试时使用的是内部RC振荡器时钟。

在这里插入图片描述

由STM8S系列参考手册时钟章节,时钟树上无对时钟进行倍频的单元,因此HSE或者HSI都是16M时钟输入,如果不进行分频,那么同样对CPU时钟而言都是16M。为了方便 (懒。。。),在初步测试时使用的是内部RC振荡器16M。

在这里插入图片描述

硬件IIC


从STM8S103数据手册上看I2C有两种支持速率:最高到100K和400K

在这里插入图片描述

使用的是PB5复用I2C_SDA,PB4复用I2C_SCL

在这里插入图片描述

需要注意的是:选项字节中的OPT2中的AFR4需要是0,如果不是0则PB4和PB5不是复用为I2C引脚

在这里插入图片描述

另外,需要注意的是,由下图看:

I2C在100K和400K速率下对时钟要求不同,且。。且。。。且主机用的是IO口模拟IIC,必须符合相应的最短电平持续时间

还有一点:如果使用400K速度,那么主时钟至少是8M

在这里插入图片描述

硬件IIC库


从IIC库的相关文件上开放了一些接口,我仅用到了两个,就只说这两个

在这里插入图片描述

I2C_Init


void I2C_Init(uint32_t OutputClockFrequencyHz, uint16_t OwnAddress, 

     I2C_DutyCycle_TypeDef I2C_DutyCycle, I2C_Ack_TypeDef Ack, 

     I2C_AddMode_TypeDef AddMode, uint8_t InputClockFrequencyMHz )


功能:I2C硬件初始化

参数:OutputClockFrequencyHz ----- 输出时钟频率

OwnAddress-------本I2C设备地址

I2C_DutyCycle-----占空比,高低电平的时间,和速率有关,详情看手册CCR寄存器

Ack---------应答模式,接收一个字节数据后是否产生应答

AddMode----采用7/10位地址设置

InputClockFrequencyMHz----提供给I2C硬件时钟


I2C_ITConfig


void I2C_ITConfig(I2C_IT_TypeDef I2C_IT, FunctionalState NewState)


功能:I2C的中断配置

参数:I2C_IT ----- 中断类型,如接收到本机地址则产生中断

NewState----中断使能/关闭


四.代码


代码利用测试板和测试过,跑的是100K的速率,能正常运行


CPU时钟:分频配置为不分频,16M


CLK_HSIPrescalerConfig(CLK_PRESCALER_HSIDIV1);


I2C硬件初始化


   u32 CPU_CLK;// CPU时钟

   // GPIOB4作为上拉无中断输入,GPIOB5高阻态快速输出(10M大小)

   GPIO_Init(GPIOB,GPIO_PIN_4, GPIO_MODE_IN_PU_NO_IT);

   GPIO_Init(GPIOB,GPIO_PIN_5, GPIO_MODE_OUT_OD_HIZ_FAST);

   // 获取当前CPU时钟,M为单位

   CPU_CLK = CLK_GetClockFreq()/1000000;

   /* 

   I2C初始化:

   不需输出时钟,随便写个400000,设备地址0XA0,实际上最后一位是读写控制位

    每接收一个字节回应答,7位地址模式,输入I2C时钟是上面的CPU_CLK

   */

   I2C_Init(400000, 0XA0 , I2C_DUTYCYCLE_2, I2C_ACK_CURR, I2C_ADDMODE_7BIT, CPU_CLK);//I2C初始化

   /*

    I2C中断配置:

    开启 错误中断、事件中断(如匹配了地址、接收了一字节数据等)、BUF中断(接收和发送相关)

    */

   I2C_ITConfig((I2C_IT_TypeDef)(I2C_IT_ERR | I2C_IT_EVT | I2C_IT_BUF), ENABLE) ;  


开总中断


__enable_interrupt();


I2C中断函数

在写中断函数之前,先看一下参考手册关于I2C硬件的部分功能描述


I2C接口在接收到地址并且匹配上时,根据ACK可以自动产生一个应答信号,且会将ADDR标志写1,产生一个中断


总结:I2C设备接收到别人发过来的地址,匹配上时,可以自动应答,并且产生中断。

但是,如何对本设备进行读和写的区分呢?因为本次我们配置的是7位地址模式,上面配置本机地址为0XA0,按照最后一位是0还是1,I2C硬件自动识别是读还是写的动作。如果起始信号后,主机发送0XA0,那么主机对从机进行写操作;如果起始信号后,主机发送0XA1,那么主机对从机进行读操作。

在这里插入图片描述

从设备接收模式(主机发送、从机接收,因此DR寄存器用来接收数据)

可以得出信息:


 .接收到地址后,要清除ADDR标志

 .在清除标志后,才会从移位寄存器的数据放到DR寄存器上(因此,接收时要及时清除ADDR标志)

 .每接收一个字节数据可以自动应答

 .接收一个数据后,RxNE位置1,可产生中断

 注意:在接收数据后,DR寄存器没被读出时,I2C接口SCL是一直保持为低电平的,这个时候,主机进行发送是没效果的。因此要在中断中及时读取走DR寄存器

在这里插入图片描述

EV1是在地址应答阶段后,解释说明指出要清除ADDR,通过读SR1和SR3

EV2是每接收到一个数据,要通过读DR来清除,如果不读则出现上述情况

EV4是接收到停止信号后,通过读SR1,写CR2寄存器清除标志


在这里插入图片描述

从设备发送模式(主机接收、从机发送,因此DR寄存器用来发送数据)

可以得出信息:


.在接收地址后并清除了ADDR标志位后,从设备才会将DR寄存器发出

.如果ADDR标志不清除、DR寄存器没及时写入,SCL会一直被拉低,主机操作无效果(特别重要,清除标志,对DR寄存器写操作越快越往前越好)

.如果发送完成,可以产生中断

在这里插入图片描述

请注意地址应答后的阶段是 EV1/EV3-1/EV3,这个时候就是需要及时清标志位和写DR寄存器进行发送。如果不呢?


像我当时在中断中还执行了一些语句,并没有把写DR寄存器放到靠前位置,也有一些多余未优化的语句,导致从逻辑分析仪的图形中可以明显看出SCL被一直拉低了,其实我在主机IO模拟中,应答动作后SCL拉低没有那么长的延时的,而是很规律的时钟。


在这里插入图片描述

状态寄存器

SR1:

TXE-------发送状态,如果DR寄存器没有发送,那么置1

RXNE----接收到数据,置1

STOPF–接收到停止信号,置1

BTF-----接收到数据/发送数据完成,但还没读取/再次写入

ADDR–匹配到设备地址

在这里插入图片描述

SR2:一些错误标志位

在这里插入图片描述

SR3:

TRA:处于接收还是发送的状态

在这里插入图片描述

代码:由于一些原因,将I2C的数据包格式协议这些用一些变量和数组代替,有需要用的记得根据自己的需求改动


// 测试相关

u8 Test_read[50] = “HELLO ,THANK U,THANK U VERY MUCH”;

u8 Test_r_num = 31; //从机发送个数

u8 Test_r_pos = 0;   // 发送指针

u8 Test_Write[50] = {0};

u8 Test_w_num = 0;   // 主机发送数据个数

u8 Commucation_flag = 0;// 通信完成标志


/**

  * @brief I2C Interrupt routine.

  * @param  None

  * @retval None

  */

INTERRUPT_HANDLER(I2C_IRQHandler, 19)

//void IIC_Interrupt(void)

{

  /* In order to detect unexpected events during development,

     it is recommended to set a breakpoint on the following instruction.

  */

       //接收发送   

      if (I2C->SR1&0x02)//地址已经匹配 ADDR标志

      {          

      // 清ADDR标志:读SR1,读SR3

      // 主机读,从机发出的情况,DR寄存器写数据越快越好

      // 判断是否是读操作 TRA

          if ((I2C->SR3)&0x04)

          {        

               if (Test_r_pos < Test_r_num)

               {

                      I2C->DR = Test_read[Test_r_pos ];

                      Test_r_pos ++;

               }

               else 

                    I2C->DR = 0XFF;

                        

          }

          else

          {

  Test_r_pos=0; 

 }

          Test_w_num= 0;

      }

      else if ((I2C->SR3)&0x04)//如果是接收状态:

      {        

       // 查看TXE是否DR为空

          if ((((I2C->SR1)&0x80) == 0X80))        

          {

               if (Test_r_pos < Test_r_num)

               {

                      I2C->DR = Test_read[Test_r_pos ];

                      Test_r_pos ++;

               }

              else 

              I2C->DR = 0XFF;           

          }              

      }

      else if (((I2C->SR1)&0x40)&&(!((I2C->SR3)&0x04)))

      {

      // 读DR,清接收标志

      u8 data = I2C->DR;

      // 如果主机发送,从机接收到数据

          if (Test_w_num < 50)

            Test_Write[Test_w_num ++] = data ;

      }

      else if ((I2C->SR1)&0x10)

      {

      //检测到停止位----清除停止位---通过写CR2

          I2C->CR2 = I2C->CR2;

          // 停止标志位置

          Test_r_pos = 0;

          Commucation_flag = 1;

      }

      

      // 错误管理

      if ((I2C->SR2)&0X0F)           

      {

          I2C->SR2 &= ~(0X0F); // 清零

      }

  

}

推荐阅读

史海拾趣

全智景(Allvision)公司的发展小趣事

随着技术的不断进步,全智景公司开始寻求跨界合作的机会,以拓展其技术的应用领域。公司与多家汽车制造商达成了合作协议,将其电子视觉技术应用于汽车自动驾驶系统。通过精准的图像识别和处理,全智景的技术帮助汽车实现了更高级别的自动驾驶功能,提升了行车安全性和驾驶体验。这一跨界合作不仅拓宽了全智景公司的业务范围,也为其带来了更多的商业机会。

功得(CONQUER)公司的发展小趣事

在市场竞争日益激烈的环境下,功得公司意识到仅仅依靠创新是不够的,还需要有高品质的产品来赢得客户的信任。因此,公司开始注重产品质量管理,建立了完善的质量控制体系。功得公司严格把控原材料采购、生产工艺和成品检验等环节,确保每一件产品都符合高标准的质量要求。这种对品质的执着追求,使得功得公司的产品在市场上赢得了良好的口碑,品牌知名度也逐渐提升。

Hexawave公司的发展小趣事

随着公司实力的不断增强,功得公司开始将目光投向国际市场。他们积极参加国际电子展会,与全球各地的客户建立联系,拓展销售渠道。同时,功得公司还根据不同国家和地区的市场需求,推出定制化的产品和服务。通过不懈努力,功得公司逐渐在国际市场上打开了局面,产品出口量稳步增长。

DOMINANT公司的发展小趣事

在LED产业蓬勃发展的背景下,统明亮(DOMINANT)公司在2000年应运而生。它的诞生源于欧司朗(OSRAM)在马六甲工厂的分拆,加上马来西亚财团的加持,这家新公司迅速崭露头角。统明亮公司从成立之初就专注于LED技术的研发和生产,凭借其卓越的技术实力和创新能力,逐渐在电子行业中树立了良好的口碑。经过数年的不懈努力,统明亮在全球贴片式LED领域取得了领先地位,特别是在车用LED封装领域,其产品质量和技术水平均处于行业前列。

Dawning Precision Co Ltd公司的发展小趣事

随着市场竞争的加剧,Dawning意识到只有不断创新才能在行业中立于不败之地。公司加大了对研发的投入,聘请了一批在电子制造领域具有丰富经验的专家。在专家团队的带领下,Dawning成功开发出了多款具有自主知识产权的高精度电子产品,这些产品不仅在性能上领先同行,而且在价格上也更具竞争力。这些创新产品的推出,极大地提升了Dawning在市场上的竞争力。

Bergquist Company公司的发展小趣事

70年代,随着电子技术的飞速发展,导热材料行业迎来了前所未有的机遇。Bergquist Company敏锐地捕捉到了这一机遇,开始涉足导热材料领域。公司投入大量资金进行研发,成功开发出了一系列具有创新性的导热产品。这些产品不仅性能卓越,而且价格合理,深受市场欢迎。凭借这些优势,Bergquist Company逐渐在导热材料行业崭露头角,成为了行业的领军企业。

问答坊 | AI 解惑

超低功耗27-MHz无线鼠标参考设计

超低功耗27-MHz无线鼠标参考设计…

查看全部问答>

一个特殊干扰的排除过程

BY 安防社区 水木金钟 有一学校,四年前安装了一套视频监控系统(PC式DVR),只有4路(只监控大门,二进二出),现在由我们公司升级到20路。     我们把所有的系统都做好后,发现有非常弱的干扰信号,不到200米的几个摄像机,在传输中 ...…

查看全部问答>

CE下字符串写入文件出错,怎么办那?

我在CE下要向文件里写一些字符串。可是写入的时候出错,我以前用这代码在WIN32下好使的。请问怎么办 CStdioFile* pFile = new CStdioFile(FileName,CFile::modeWrite|CFile::modeCreate); CString code; code.Format(L\"P0%d  X%f, Y% ...…

查看全部问答>

vs2005 mfc 基于智能设备的程序 如何在按钮上显示位图

里面不能用setbitmap()大家都用什么方法 阿 …

查看全部问答>

在线调试问题!

用过万隆的开发板的兄弟们,在线调试能行吗?交流一下!…

查看全部问答>

怎样实现 防拆卸功能????

有个小仪器,需要实现防拆卸功能,非法拆卸就报警。 机械设计需要另外开模具,花费有点大,不知道还有什么方法能实现? …

查看全部问答>

耦合电感拓展DC/DC 转换器的应用

     尽管对于能够对高低输入电压之间的输出电压(例如:未经调节的墙上 12V 电源)进行调节的转换器需求一直都存在,而且 DC/DC 单端初级电感转换器(SEPIC) 拓扑也不是什么新东西,但的确直到最近它才开始流行起来。   ...…

查看全部问答>

关于realView MDK资料整理。收藏版

关于realView MDK资料整理。收藏版…

查看全部问答>

数码管显示问题

各位大侠,请问我用定时中断去显示数码管,0-60,为什么数码管总会有个二极管不亮的? 图:数码管应该显示02的!可是总缺失一个部位! 有高手可以帮忙么? 谢谢! 代码如下: #include #define uint unsigned int #define uchar unsigned ch ...…

查看全部问答>

开关电源的啸叫~

用的是TPS63020,电感是XFL4020-152 1.5uH 上电后出现刺耳的啸叫........= =;;;...而且烫死了..我就给立马断开了,,,用的EN引脚做开关 LAYOUT也是按照官方的来的....,,哪地方可能出问题了呢...........纠结..…

查看全部问答>