小端和大端存储:
注意一个内存地址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类型均相同