title: Smart way to transform stm32 function into class
body: <p>I'm trying to build a class for stm32 peripheral function like i2c, uart. But I ran into some situation where I don't know what to do is the best way.</p>
  <p>Below is my I2C init function:</p>
  <pre><code>void init_I2C1(void)
  {
      /* RCC initialization */
      RCC_APB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE);
      RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C1, ENABLE);
  
      GPIO_InitTypeDef GPIO_InitStruct = {
          .GPIO_Pin = GPIO_Pin_6 | GPIO_Pin_7,
          .GPIO_Mode = GPIO_Mode_AF,
          .GPIO_Speed = GPIO_Speed_50MHz,
          .GPIO_OType = GPIO_OType_PP,
          .GPIO_PuPd = GPIO_PuPd_UP
      };
      GPIO_Init(GPIOB, &amp;GPIO_InitStruct);
  
      GPIO_PinAFConfig(GPIOB, GPIO_PinSource6, GPIO_AF_I2C1);
      GPIO_PinAFConfig(GPIOB, GPIO_PinSource7, GPIO_AF_I2C1);
  
      I2C_InitTypeDef I2C_InitStruct = {
          .I2C_ClockSpeed = 100000,
          .I2C_Mode = I2C_Mode_I2C,
          .I2C_DutyCycle = I2C_DutyCycle_2,
          .I2C_OwnAddress1 = 0x75,
          .I2C_Ack = I2C_Ack_Enable,
          .I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit
      };
      I2C_Init(I2C1, &amp;I2C_InitStruct);
  
      I2C_Cmd(I2C1, ENABLE);
  }
  </code></pre>
  <p>But There is 3 pair of I2C that I can use, so I want to write a general class so I can just specify the number to the class instead of wirte 3 similar functions.</p>
  <p>Therefore, I need to find a way to replace those constants.
  For those constant just represent a continuous value, I can just use bit shift operation.</p>
  <p>ex.</p>
  <pre><code>#define GPIO_PinSource6            ((uint8_t)0x06)
  #define GPIO_PinSource7            ((uint8_t)0x07)
  </code></pre>
  <p>But other constants are complicated. They are pointers of some structure, so I cannot just just bit shift them. And there are not array either so I also cannot use an interger to go through.</p>
  <p>ex.</p>
  <pre><code>#define PERIPH_BASE           ((uint32_t)0x40000000)
  #define APB1PERIPH_BASE       PERIPH_BASE
  
  #define I2C1_BASE             (APB1PERIPH_BASE + 0x5400)
  #define I2C2_BASE             (APB1PERIPH_BASE + 0x5800)
  
  #define I2C1                ((I2C_TypeDef *) I2C1_BASE)  //the constant I need to use
  #define I2C2                ((I2C_TypeDef *) I2C2_BASE)  //the constant I need to use
  </code></pre>
  <p>The solution I have now is to trace back to the numerical value then do the bit shifting thing.For example,</p>
  <pre><code>I2C_TypeDef * I2C_Type_Ptr = (I2C_TypeDef *)(APB1PERIPH_BASE + (0x5400 &lt;&lt; i2c_number));
  I2C_Init(I2C_Type_Ptr, &amp;I2C_InitStruct);
  </code></pre>
  <p>But it looks ugly and stupid. I wonder whether there is a more efficient way to do this.</p>
  <p>Here is the whole code of the class I have done so far.</p>
  <pre><code>//Just for I2C1 &amp; I2C2, there all use portB
  I2C::I2C(uint8_t number, uint8_t port_SCL, uint8_t port_SDA)
  {
      /* RCC initialization */
      RCC_APB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE);
      RCC_APB1PeriphClockCmd((RCC_APB1Periph_I2C1 &lt;&lt; (number - 1)), ENABLE);
  
      GPIO_InitTypeDef GPIO_InitStruct = {
          .GPIO_Pin = (GPIO_Pin_0 &lt;&lt; port_SCL) | (GPIO_Pin_0 &lt;&lt; port_SDA),
          .GPIO_Mode = GPIO_Mode_AF,
          .GPIO_Speed = GPIO_Speed_50MHz,
          .GPIO_OType = GPIO_OType_PP,
          .GPIO_PuPd = GPIO_PuPd_UP
      };
      GPIO_Init(GPIOB, &amp;GPIO_InitStruct);
  
      GPIO_PinAFConfig(GPIOB, (GPIO_PinSource0 + port_SCL), GPIO_AF_I2C1);  //all I2C AF selection are all the same (0x04)
      GPIO_PinAFConfig(GPIOB, (GPIO_PinSource0 + port_SDA), GPIO_AF_I2C1);
  
      I2C_InitTypeDef I2C_InitStruct = {
          .I2C_ClockSpeed = 100000,
          .I2C_Mode = I2C_Mode_I2C,
          .I2C_DutyCycle = I2C_DutyCycle_2,
          .I2C_OwnAddress1 = 0x75,
          .I2C_Ack = I2C_Ack_Enable,
          .I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit
      };
      I2C_TypeDef * I2C_Type_Ptr = (I2C_TypeDef *)(APB1PERIPH_BASE + (0x5400 &lt;&lt; number));
      I2C_Init(I2C_Type_Ptr, &amp;I2C_InitStruct);
  
      I2C_Cmd(I2C_Type_Ptr, ENABLE);
  }
  </code></pre>
  <p>Thank you for your help~~</p>
  <p>(P.S. The library I use is &quot;STM32F4xx_StdPeriph_Driver&quot;)</p>