小端和大端存储:

注意一个内存地址8位,因此8位8位的取

数据类型

强制类型转换并不会改变变量的数据类型和数值

本例子来源于ST官方函数

1 //stm32f10x_flash.c
2 FLASH_Status FLASH_ProgramWord(uint32_t Address, uint32_t Data)

我们知道Flash只能一次性写入16位数据,但是它是如何写入32位数据的呢

关键的程序如下

1 *(__IO uint16_t*)Address = (uint16_t)Data;//首地址是Address
2  tmp = Address + 2;//存完低16位后向后移两位的地址存放高16位
3 *(__IO uint16_t*) tmp = Data >> 16;

 (uint16_t)Data相当于将Data放在一个16位的临时变量中,这相当于将它的低16位放到了这个临时变量中,临时变量再将这个低16位的值放到Address地址下的uint16_t内存中(注意stm32向内存中写入遵循的是小端模式,因此向写入低16位)(注意长位数据向短位数据中写入数据,长位的多出来的高位会被去掉)但是Data本身仍然是32位数据,它的数值和类型完全不变

Data>>16相当于高16位将低16位覆盖,这样Data就变成了数值是Data高16位,位数为16的数据,这样在将数据放在以tmp为起始地址的16位内存在,这样就将这个32位数据写入了Flash中

如果你不信的话我们举个实际例子:

 1 int main()
 2 {
 3     OLED_Init();
 4     uint32_t num=0x00010001;
 5     uint16_t value1=(uint16_t)num;
 6     uint32_t temp=num;
 7     uint8_t num1;
 8     uint8_t num2;
 9     uint8_t num3;
10     uint8_t num4;
11     num1=(0xff000000&temp)>>24;
12     num2=(0x00ff0000&temp)>>16;
13     num3=(0x0000ff00&temp)>>8;
14     num4=(0x000000ff&temp);
15 
16     OLED_Printf(0,0,OLED_6X8,"temp %d",temp);
17     OLED_Printf(0,8,OLED_6X8,"num1 %d",num1);
18     OLED_Printf(0,16,OLED_6X8,"num2 %d",num2);
19     OLED_Printf(0,24,OLED_6X8,"num3 %d",num3);
20     OLED_Printf(0,32,OLED_6X8,"num4 %d",num4);
21     OLED_Update();
22     return 0;
23 }

强制类型转换并不会改变指针的类型和指针所指向的值的类型

 1 int main()
 2 {
 3     OLED_Init();
 4     uint32_t num=0x00010001;
 5     uint32_t*Pnum=#
 6     uint16_t*Pvalue=(uint16_t*)Pnum;
 7     uint8_t num1;
 8     uint8_t num2;
 9     uint8_t num3;
10     uint8_t num4;
11     num1=(0xff000000&(*Pnum))>>24;
12     num2=(0x00ff0000&(*Pnum))>>16;
13     num3=(0x0000ff00&(*Pnum))>>8;
14     num4=(0x000000ff&(*Pnum));
15 
16     OLED_Printf(0,0,OLED_6X8,"temp %d",(*Pnum));
17     OLED_Printf(0,8,OLED_6X8,"num1 %d",num1);
18     OLED_Printf(0,16,OLED_6X8,"num2 %d",num2);
19     OLED_Printf(0,24,OLED_6X8,"num3 %d",num3);
20     OLED_Printf(0,32,OLED_6X8,"num4 %d",num4);
21     OLED_Update();
22     return 0;
23 }

 

我们用C++确认一下

1 int main()
2 {
3     int a = 10;
4     int* Pa = &a;
5     float* temp = (float*)Pa;
6     cout << typeid(a).name() << endl;
7     cout << typeid(Pa).name() << endl;
8     return 0;
9 }

 

如何取出整型的每一位

我们可以定义四个uint8_t的变量,然后将int类型变量每8位每8位的取出

 1 int main()
 2 {
 3     OLED_Init();
 4     int num=261;
 5     uint8_t num1;
 6     uint8_t num2;
 7     uint8_t num3;
 8     uint8_t num4;
 9     num1=(0xff000000&num)>>24;
10     num2=(0x00ff0000&num)>>16;
11     num3=(0x0000ff00&num)>>8;
12     num4=(0x000000ff&num);
13 
14     OLED_Printf(0,0,OLED_6X8,"num %d",num);
15     OLED_Printf(0,8,OLED_6X8,"num1 %d",num1);
16     OLED_Printf(0,16,OLED_6X8,"num2 %d",num2);
17     OLED_Printf(0,24,OLED_6X8,"num3 %d",num3);
18     OLED_Printf(0,32,OLED_6X8,"num4 %d",num4);
19     OLED_Update();
20     return 0;
21 }

如何向内存中存放数据

1 *((__IO uint16_t*)Adress);//STM32中地址是32位的,该语句并没有改变地址的类型,只是表明有个无符号16位的指针指向该地址,该地址存放着无符号16位的数值

如何取出浮点型每一位

单精度浮点型

单精度浮点型在内存中按照IEEE754存储

比如19.625为01000001100111010000000000000000

浮点型不可以参与位运算,因此我们还要对它进行特殊的处理,方法是使用联合体让一个uint32_t的变量和它共用一块内存,这样这个uint32_t的变量在内存中的二进制形式和浮点型的一模一样,然后我们可以定义四个uint8_t的变量,然后将uint32_t类型变量每8位每8位的取出

 1 union test
 2 {
 3     float value;
 4     uint32_t num;
 5 }Test;
 6 int main()
 7 {
 8     Test.value=19.625;
 9     OLED_Init();
10     uint8_t num1;
11     uint8_t num2;
12     uint8_t num3;
13     uint8_t num4;
14     num1=(0xff000000&(Test.num))>>24;//65(十进制)41(16进制)
15     num2=(0x00ff0000&(Test.num))>>16;//157(十进制)9D(16进制)
16     num3=(0x0000ff00&(Test.num))>>8;//0
17     num4=(0x000000ff&(Test.num));
18     OLED_Printf(0,0,OLED_6X8,"num %f",Test.value);
19     OLED_Printf(0,8,OLED_6X8, "num1 %d",num1);
20     OLED_Printf(0,16,OLED_6X8,"num2 %d",num2);
21     OLED_Printf(0,24,OLED_6X8,"num3 %d",num3);
22     OLED_Printf(0,32,OLED_6X8,"num4 %d",num4);
23     OLED_Update();
24     return 0;
25 }

项目问题1:

写入Flah

以为例我们如何将浮点型和整型写入Flash里面呢,我们首先想到的是利用FLASH_Status FLASH_ProgramWord(uint32_t Address, uint32_t Data):

1 float value=19.625;
2 MyFLASH_ErasePage(0x0800FC00);
3 MyFLASH_ProgramWord(0x0800FC00, value);//核心函数是FLASH_Status FLASH_ProgramWord(uint32_t Address, uint32_t Data)
4 OLED_Printf(35,17,OLED_8X16,"%05.3f",MyFLASH_ReadFloat(0x0800FC00));
5 OLED_Update();

我们预期的结果:

我们实际的结果:

为什么会这样呢,原因是变量以什么形式存在Flash中是它前面的数据类型说的算,float value将数值传给形参uint32_t Data以后uint32_t Data将19.625以uint32_t形式存在Flash中(也就是我们实际的结果),根本就没有以float形式存在Flash中(我们预期的结果)

我们以联合体解决上述问题

union test
{
    float value;
    uint32_t b;
}Test;
int main(void)
{
    Myiic_Init();
    OLED_Init();

    Test.value=19.625;
    MyFLASH_ErasePage(0x0800FC00);
    MyFLASH_ProgramWord(0x0800FC00, Test.b);
    OLED_Printf(35,17,OLED_8X16,"%05.3f",MyFLASH_ReadFloat(0x0800FC00));
    OLED_Update();

}

原理是b和value共用一块内存,value先将19.625以float形式存到这块内存,当b使用时并不会改变这块内存里的内容,也就是这块内存的二进制串没有发生任何改变,当传给Data的时候Data也是接收到的这个二进制串,后续的处理也一直都没有改变,因此储存在Flash中的还是那个二进制串

或使用指针(更好)

 

 1 int main()
 2 {
 3     float data=19.625;
 4     uint8_t*pdata=(void*)&data;
 5     OLED_Init();
 6     OLED_Clear();
 7     OLED_Printf(0,0,OLED_8X16,"%d",*pdata);
 8     OLED_Printf(0,16,OLED_8X16,"%d",*(pdata+1));//也可以pdata[1]
 9     OLED_Printf(0,32,OLED_8X16,"%d",*(pdata+2));
10     OLED_Printf(0,48,OLED_8X16,"%d",*(pdata+3));    
11     OLED_Update();
12     return 0;
13 }

读Flash

读Flash中的数据和读取的形式以及打印的形式有关:

 1 uint32_t MyFLASH_ReadWord(uint32_t Adress)
 2 {
 3     return *((__IO uint32_t*)Adress);//以uint32_t形式取出数据
 4 }
 5 
 6 float MyFLASH_ReadFloat(uint32_t Adress)
 7 {
 8     return *((__IO float*)Adress);//以float形式取出数据
 9 }
10 
11 OLED_Printf(35,17,OLED_8X16,"%u",MyFLASH_ReadWord(0x0800FC00));//以uint32_t形式打印数据
12 
13 OLED_Printf(35,17,OLED_8X16,"%06.3f",MyFLASH_ReadWord(0x0800FC00));//以float形式打印数据

总结起来:直接从内存中取数据,打印数据,向内存中写数据(别忘了数组),一定要注意数据的储存方式

以上方法也可以用于EEPROM

项目问题2:

操作EEPROM的时候我们可以定义一个缓存数组,当数组中存储了一定量的数据的时候我们一起将数组写入EEPROM中,那我们如何将float或则int类型的数组写入EEPROM中呢(EEPROM是一个字节一个字节的存入数据)

程序来自野火

  1 I2C_EE_BufferWrite((void*)double_buffer,DOUBLE_ADDR, sizeof(double_buffer));
  2 I2C_EE_BufferWrite((void*)int_bufffer, LONGINT_ADDR, sizeof(int_bufffer));
  3 
  4 
  5 void I2C_EE_BufferWrite(u8* pBuffer, u8 WriteAddr, u16 NumByteToWrite)
  6 {
  7   u8 NumOfPage = 0, NumOfSingle = 0, Addr = 0, count = 0;
  8 
  9   Addr = WriteAddr % I2C_PageSize;
 10   count = I2C_PageSize - Addr;
 11   NumOfPage =  NumByteToWrite / I2C_PageSize;
 12   NumOfSingle = NumByteToWrite % I2C_PageSize;
 13  
 14   /* If WriteAddr is I2C_PageSize aligned  */
 15   if(Addr == 0) 
 16   {
 17     /* If NumByteToWrite < I2C_PageSize */
 18     if(NumOfPage == 0) 
 19     {
 20       I2C_EE_PageWrite(pBuffer, WriteAddr, NumOfSingle);
 21       I2C_EE_WaitEepromStandbyState();
 22     }
 23     /* If NumByteToWrite > I2C_PageSize */
 24     else  
 25     {
 26       while(NumOfPage--)
 27       {
 28         I2C_EE_PageWrite(pBuffer, WriteAddr, I2C_PageSize); 
 29         I2C_EE_WaitEepromStandbyState();
 30         WriteAddr +=  I2C_PageSize;
 31         pBuffer += I2C_PageSize;
 32       }
 33 
 34       if(NumOfSingle!=0)
 35       {
 36         I2C_EE_PageWrite(pBuffer, WriteAddr, NumOfSingle);
 37         I2C_EE_WaitEepromStandbyState();
 38       }
 39     }
 40   }
 41   /* If WriteAddr is not I2C_PageSize aligned  */
 42   else 
 43   {
 44     /* If NumByteToWrite < I2C_PageSize */
 45     if(NumOfPage== 0) 
 46     {
 47       I2C_EE_PageWrite(pBuffer, WriteAddr, NumOfSingle);
 48       I2C_EE_WaitEepromStandbyState();
 49     }
 50     /* If NumByteToWrite > I2C_PageSize */
 51     else
 52     {
 53       NumByteToWrite -= count;
 54       NumOfPage =  NumByteToWrite / I2C_PageSize;
 55       NumOfSingle = NumByteToWrite % I2C_PageSize;    
 56       
 57       if(count != 0)
 58       {  
 59         I2C_EE_PageWrite(pBuffer, WriteAddr, count);
 60         I2C_EE_WaitEepromStandbyState();
 61         WriteAddr += count;
 62         pBuffer += count;
 63       } 
 64       
 65       while(NumOfPage--)
 66       {
 67         I2C_EE_PageWrite(pBuffer, WriteAddr, I2C_PageSize);
 68         I2C_EE_WaitEepromStandbyState();
 69         WriteAddr +=  I2C_PageSize;
 70         pBuffer += I2C_PageSize;  
 71       }
 72       if(NumOfSingle != 0)
 73       {
 74         I2C_EE_PageWrite(pBuffer, WriteAddr, NumOfSingle); 
 75         I2C_EE_WaitEepromStandbyState();
 76       }
 77     }
 78   }  
 79 }
 80 
 81 
 82 uint32_t I2C_EE_PageWrite(u8* pBuffer, u8 WriteAddr, u8 NumByteToWrite)
 83 {
 84   I2CTimeout = I2CT_LONG_TIMEOUT;
 85 
 86   while(I2C_GetFlagStatus(EEPROM_I2Cx, I2C_FLAG_BUSY))   
 87   {
 88     if((I2CTimeout--) == 0) return I2C_TIMEOUT_UserCallback(4);
 89   } 
 90   
 91   /* Send START condition */
 92   I2C_GenerateSTART(EEPROM_I2Cx, ENABLE);
 93   
 94   I2CTimeout = I2CT_FLAG_TIMEOUT;
 95   /* Test on EV5 and clear it */
 96   while(!I2C_CheckEvent(EEPROM_I2Cx, I2C_EVENT_MASTER_MODE_SELECT))  
 97   {
 98     if((I2CTimeout--) == 0) return I2C_TIMEOUT_UserCallback(5);
 99   } 
100   
101   /* Send EEPROM address for write */
102   I2C_Send7bitAddress(EEPROM_I2Cx, EEPROM_ADDRESS, I2C_Direction_Transmitter);
103   
104   I2CTimeout = I2CT_FLAG_TIMEOUT;
105   /* Test on EV6 and clear it */
106   while(!I2C_CheckEvent(EEPROM_I2Cx, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED))  
107   {
108     if((I2CTimeout--) == 0) return I2C_TIMEOUT_UserCallback(6);
109   } 
110   
111   /* Send the EEPROM's internal address to write to */    
112   I2C_SendData(EEPROM_I2Cx, WriteAddr);  
113 
114   I2CTimeout = I2CT_FLAG_TIMEOUT;
115   /* Test on EV8 and clear it */
116   while(! I2C_CheckEvent(EEPROM_I2Cx, I2C_EVENT_MASTER_BYTE_TRANSMITTED))
117   {
118     if((I2CTimeout--) == 0) return I2C_TIMEOUT_UserCallback(7);
119   } 
120 
121   /* While there is data to be written */
122   while(NumByteToWrite--)  
123   {
124     /* Send the current byte */
125     I2C_SendData(EEPROM_I2Cx, *pBuffer); 
126 
127     /* Point to the next byte to be written */
128     pBuffer++; 
129   
130     I2CTimeout = I2CT_FLAG_TIMEOUT;
131 
132     /* Test on EV8 and clear it */
133     while (!I2C_CheckEvent(EEPROM_I2Cx, I2C_EVENT_MASTER_BYTE_TRANSMITTED))
134     {
135       if((I2CTimeout--) == 0) return I2C_TIMEOUT_UserCallback(8);
136     } 
137   }

我们将上述问题简化

void fun(uint8_t* pArr) {
//    OLED_Printf(0,8,OLED_6X8,"pArr size %d",(sizeof(pArr) / sizeof(pArr[0])));
    OLED_Printf(0,0,OLED_6X8,"pArr[0] %d",pArr[0]);
    OLED_Printf(0,8,OLED_6X8,"pArr[1] %d",pArr[1]);
    OLED_Printf(0,16,OLED_6X8,"pArr[2] %d",pArr[2]);
    OLED_Printf(0,24,OLED_6X8,"pArr[3] %d",pArr[3]);
    OLED_Printf(0,32,OLED_6X8,"pArr[4] %d",pArr[4]);
    OLED_Printf(0,40,OLED_6X8,"pArr[5] %d",pArr[5]);
    OLED_Printf(0,48,OLED_6X8,"pArr[6] %d",pArr[6]);
    OLED_Printf(0,56,OLED_6X8,"pArr[7] %d",pArr[7]);
}
int main()
{
    OLED_Init();
    float arr[2] = { 19.625,65.875 };
//    OLED_Printf(0,0,OLED_6X8,"arr size %d",(sizeof(arr) / sizeof(arr[0])));
    fun((void*)arr);
    OLED_Update();
    return 0;
}

首先注意写入数组也相当于写入内存是小端存储

 1 int main()
 2 {
 3     OLED_Init();
 4     uint8_t arr[8]={0,0,157,65,0,192,131,66};
 5     float *parr=(void*)arr;
 6     OLED_Clear();
 7     OLED_Printf(0,0,OLED_6X8,"parr[0] %05.3f",parr[0]);
 8     OLED_Printf(0,8,OLED_6X8,"parr[1] %05.3f",parr[1]);
 9     OLED_Update();
10 }

原理:

红色是存入EEPROM的过程(float数组-->uint8_t数组),铅笔是从EEPROM的过程(uint8_t数组-->float数组)

从上述可知指针存储数组只和指针的类型有关,因此int类型和float类型均相同

 

posted on 2023-12-13 14:52  小凉拖  阅读(15)  评论(0编辑  收藏  举报