单片机学习小组
直播中

李娟

7年用户 1375经验值
私信 关注

如何去实现IIC总线上挂载多个从机的程序呢

I2C总线有哪些特性呢?
如何去实现IIC总线上挂载多个从机的程序呢?

回帖(1)

张萍

2022-2-24 15:46:23
IIC总线上挂载多个从机的程序实现



IIC简介:




1、I2C总线具有两根双向信号线,一根是数据线SDA,另一根是时钟线SCL

2、IIC总线上可以挂很多设备:多个主设备,多个从设备(外围 设备)。

3、多主机会产生总线裁决问题。当多个主机同时想占用总线时,企图启动总线传输数据,就叫做总线竞争。I2C通过总线仲裁,以决定哪台主机控制总线

在一般的项目中,一般不会涉及到IIC总线上挂载多主机多从机的情况。但挂载单个主机多个从机的情况还是有的。

在嵌入式领域:要不专注于硬件的设计,要不专注于软件的实现。当然最好两者都兼备。相对来说,软件层面的复杂度会相对高一点,在项目开发所占的任务比重也比较大,特别是一些组网的大型项目,往往还会涉及到上位机测试软件的开发等。本篇主要讲述STM32系列单片机的IIC挂载多个从机的程序实现。

1、项目的硬件参考威廉希尔官方网站 :




如上图,将LIS2HH12加速度传感器、LPS25HB气压传感器通过IIC总线相连,与STM32Lxx 系列MCU的管脚PB6PB7 相连。

2、程序实现:


对于一个嵌入式软件工程师来说,IIC通信的基本知识需要了解,不过最重要的还是关心程序如何实现。

ST公司目前将STM32全系列都是支持HAL库的开发。cubeMx软件可以外设的驱动程序自动生成。但是多说情况下,还是需要手工改写。要不怎么称得上嵌入式软件开发工程师呢?

一般来说,对于嵌入式领域的单片机程序开发,
程序开头都会做一些初始化,初始化完成后然后进入一个死循环while(1),这对大多数没有操作系统的单片机软件来说。
我们也不例外,请看下面的程序框架:

2.1、程序框架设计:


int main(void)
{
           HAL_Init();               
        SystemClock_Config();     //系统时钟初始化
        MX_I2C1_Init();                 //外设IIC的初始化,PB6PB7,对应的外设IIC1
        /* 看门狗初始化 */
        MX_IWDG_Init();      
        HAL_IWDG_Refresh(&hiwdg);

    /*加速度传感器的配置*/
        BSP_ACC_Init(&dev_ctx);
        BSP_ACC_Config(&dev_ctx);
        /*气压计的配置及功能实现*/      
     BSP_BARO_Init(&dev_baro));
     BSP_BARO_Config(&dev_baro);
  while(1)
  {
      /*加速度传感器运用中断方式实现,所以while(1)中就出添加加速度的处理程序了*/
     
       /*气压计的处理程序*/
       BSP_BARO_handle(&dev_baro);
   }
}

2.2 IIC 总线接口程序实现:


根据威廉希尔官方网站 图和传感器的数据手册,进行如下的配置

uint8_t MX_I2C1_Init(void)  //加速度、气压传感器SensorIIC接口
{
  hi2c1.Instance = I2C1;
  hi2c1.Init.ClockSpeed = 400000;  //
  hi2c1.Init.DutyCycle = I2C_DUTYCYCLE_2;
  hi2c1.Init.OwnAddress1 = 0x34;  //这里的排至值并不影响。因为后面我们IIC的读写程序不用库函数,而是重写IIC的读写功能。
  hi2c1.Init.AddressingMode = I2C_ADDRESSINGMODE_7BIT;
  hi2c1.Init.DualAddressMode = I2C_DUALADDRESS_DISABLE;
  hi2c1.Init.OwnAddress2 = 0;//这里的排至值并不影响。因为后面我们IIC的读写程序不用库函数,而是重写IIC的读写功能。
  hi2c1.Init.GeneralCallMode = I2C_GENERALCALL_DISABLE;
  hi2c1.Init.NoStretchMode = I2C_NOSTRETCH_DISABLE;
  if (HAL_I2C_Init(&hi2c1) != HAL_OK)
  {
    _Error_Handler(__FILE__, __LINE__);
  }
      
        if( HAL_I2C_GetState( &hi2c1) == HAL_I2C_STATE_READY )
        {
                return 0;
        }
        else
        {
                return 1;
        }

}

2.3 加速度传感器的配置:


这里我们定义了传感器对象的一个结构体,将此结构体的指针指向两个IIC的读写程序platform_write、platform_read。应为传感器挂载在IIC1上,这里将传感器结构体指针的句柄定位为IIC1的地址。


I2C_HandleTypeDef  hi2c1;    //传感器的I2C接口
# define SENSOR_BUS  hi2c1

void BSP_ACC_Init(lis2hh12_ctx_t *dev_acc)
{
        dev_acc->write_reg = platform_write;
        dev_acc->read_reg =  platform_read;
        dev_acc->handle   =   &SENSOR_BUS;
        BSP_ACC_IO_ITConfig(); // 使能ACC MEMS 中断

}

//使能加速度传感器的中断管脚功能。
void BSP_ACC_IO_ITConfig( void )
{
         /* At the moment this feature is only implemented for LPS22HB */
         GPIO_InitTypeDef GPIO_InitStructureInt1;
         /* Enable INT1 GPIO clock */
         __GPIOA_CLK_ENABLE();
         /* Configure GPIO PINs to detect Interrupts */
         GPIO_InitStructureInt1.Pin   = GPIO_PIN_0;
         GPIO_InitStructureInt1.Mode  = GPIO_MODE_IT_RISING;
         GPIO_InitStructureInt1.Speed = GPIO_SPEED_MEDIUM;
         GPIO_InitStructureInt1.Pull  = GPIO_NOPULL;
         HAL_GPIO_Init(GPIOA, &GPIO_InitStructureInt1);
         /* Enable and set EXTI Interrupt priority */
         HAL_NVIC_SetPriority(EXTI0_IRQn, 4, 0x00);      
}


/************************************************************************
* 函数名称:BSP_ACC_Config(lis2hh12_ctx_t *dev_acc)
* 功    能: 加速度配置
* 输入参数:无
* 返 回 值:dev_acc 结构体
* 其    他:无
************************************************************************/

void BSP_ACC_Config(lis2hh12_ctx_t *dev_acc)
{
        /* Check device ID */
        lis2hh12_dev_id_get(dev_acc, &whoamI);
        if (whoamI != LIS2HH12_ID)
        {
                        while(1)
                        {
                                 /* manage here device not found */
                        }
        }

         /* Restore default configuration */
  lis2hh12_dev_reset_set(dev_acc, PROPERTY_ENABLE);
  do {
    lis2hh12_dev_reset_get(dev_acc, &rst);
  } while (rst);

  /* Enable Block Data Update */
  lis2hh12_block_data_update_set(dev_acc, PROPERTY_ENABLE);

  /* Set full scale */  
  lis2hh12_xl_full_scale_set(dev_acc, LIS2HH12_8g);                                                            //在加速度全域范围内运行【-8g:8g】

  /* Configure filtering chain */
  /* Accelerometer data output- filter path / bandwidth */  
  lis2hh12_xl_filter_aalias_bandwidth_set(dev_acc, LIS2HH12_AUTO);                                  // 自适应 bandwidth
  lis2hh12_xl_filter_out_path_set(dev_acc, LIS2HH12_BYPASSED);                                                         // 开启内部低通滤波
  lis2hh12_xl_filter_low_bandwidth_set(dev_acc, LIS2HH12_LP_ODR_DIV_9);                            //设置低通滤波的频率
  /* Accelerometer interrrupt - filter path / bandwidth */
  lis2hh12_xl_filter_int_path_set(dev_acc, LIS2HH12_HP_DISABLE);                         //开启 内部高通滤波

  /* Set Output Data Rate */
  lis2hh12_xl_data_rate_set(dev_acc, LIS2HH12_XL_ODR_50Hz);                                                  // ODR 设为100Hz
      
      
        #ifdef  FIFO_ACC
      
                lis2hh12_fifo_mode_set(dev_acc,LIS2HH12_STREAM_MODE);                                                         // 设置 FIFO Mode
               
                lis2hh12_fifo_watermark_set(dev_acc,PROPERTY_ENABLE);                                               //开启watermark
               
                lis2hh12_pin_int1_route_t pinValue;  
               
                pinValue.int1_drdy = 0;
                pinValue.int1_fth = 1;
                pinValue.int1_inact = 0;
                pinValue.int1_ig1 = 0;
                pinValue.int1_ig2 = 0;
                pinValue.int1_ovr = 0;

                lis2hh12_pin_int1_route_set(dev_acc,pinValue);                             //设置中断
      
          lis2hh12_fifo_watermark_set_level(dev_acc,THRESH_MASK);
      
        #endif
      
      
};

加速度传感器的中断管脚对应的中断服务函数:

在中断函数中实现计步功能。

void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{               
        if(GPIO_Pin == GPIO_PIN_0)  //
        {                 
                ACC_DRY_Flag = SET;               
                lis2hh12_handle(&dev_ctx);      
        }
}

2.4 气压传感器的配置,可以仿写加速度传感器的实现:


void BSP_ACC_Init(lps25hb_ctx_t *dev_ctx)
{
        dev_ctx.write_reg = platform_write;
        dev_ctx.read_reg = platform_read;
        dev_ctx.handle = &hi2c1;
}
void  BSP_ACC_Config(lps25hb_ctx_t  *dev_ctx)
{
    /* Check device ID */
  whoamI = 0;
  lps25hb_device_id_get(&dev_ctx, &whoamI);
  if ( whoamI != LPS25HB_ID )
    while(1); /*manage here device not found */
  /* Restore default configuration */
  lps25hb_reset_set(&dev_ctx, PROPERTY_ENABLE);
  do {
    lps25hb_reset_get(&dev_ctx, &rst);
  } while (rst);
  /*  Enable Block Data Update */
  lps25hb_block_data_update_set(&dev_ctx, PROPERTY_ENABLE);
  /* Set Output Data Rate */
  lps25hb_data_rate_set(&dev_ctx, LPS25HB_ODR_1Hz);
}

2.5气压器的处理测试程序


void  BSP_BARO_handle(lps25hb_ctx_t  *dev_ctx);
{
        while(1)
        {
      HAL_IWDG_Refresh(&hiwdg);
          /* Read output only if new value is available */
          lps25hb_reg_t reg;
          lps25hb_status_get(&dev_ctx, ®.status_reg);      
          if (reg.status_reg.p_da)
          {
            memset(data_raw_pressure.u8bit, 0x00, sizeof(int32_t));
            lps25hb_pressure_raw_get(dev_ctx, data_raw_pressure.u8bit);
            pressure_hPa = lps25hb_from_l***_to_hpa( data_raw_pressure.i32bit);      
          }      
          if (reg.status_reg.t_da)
          {
            memset(data_raw_temperature.u8bit, 0x00, sizeof(int16_t));
            lps25hb_temperature_raw_get(dev_ctx, data_raw_temperature.u8bit);
            temperature_degC = lps25hb_from_l***_to_degc( data_raw_temperature.i16bit);
          }
          HAL_Delay(500);
        }
}

3、重写IIC的读写程序


以为加速度传感器的程序为例,当然气压器的IIC读写程序可以类似仿写。

读写时将从机的地址要写入


#define LIS2HH12_I2C_ADD_L                   0x3DU
/*
* @brief  Write generic device register (platform dependent)
*
* @param  handle    customizable argument. In this examples is used in
*                   order to select the correct sensor bus handler.
* @param  reg       register to write
* @param  bufp      pointer to data to write in register reg
* @param  len       number of consecutive register to write
*
*/
static int32_t platform_write(void *handle, uint8_t reg, uint8_t *bufp,
                              uint16_t len)
{
  if (handle == &hi2c1)
  {
    HAL_I2C_Mem_Write(handle, LIS2HH12_I2C_ADD_L, reg,
                      I2C_MEMADD_SIZE_8BIT, bufp, len, 1000);
  }

  return 0;
}

/*
* @brief  Read generic device register (platform dependent)
*
* @param  handle    customizable argument. In this examples is used in
*                   order to select the correct sensor bus handler.
* @param  reg       register to read
* @param  bufp      pointer to buffer that store the data read
* @param  len       number of consecutive register to read
*
*/
static int32_t platform_read(void *handle, uint8_t reg, uint8_t *bufp,
                             uint16_t len)
{
  if (handle == &hi2c1)
  {
    HAL_I2C_Mem_Read(handle, LIS2HH12_I2C_ADD_L, reg,
                     I2C_MEMADD_SIZE_8BIT, bufp, len, 1000);
  }
  return 0;
}

ok,将代码烧录到板子上,测试成功。debug 可以看到气压计和计步值都正常输出了。

总结:


ST的Mems扩展板的示例程序上有IIC总结挂载多个从机的Demo,但是移植程序比较困难。因为需要改写很多内容。

这个示例比较简单实用。当然ST官网还提供了很多示例,根据实际项目需要可以添加需要的传感器功能。
举报

更多回帖

发帖
×
20
完善资料,
赚取积分