页面加载中,请稍候
来源:嵌入式大杂烩发布时间:2022-04-26270浏览
询问 AI一、概述




Component: ARM Compiler 5.06update6(build750) Tool: armlink [4d35ed]
==============================================================================
SectionCrossReferences
startup_stm32f103xe.o(STACK) refers (Special)toheapauxi.o(.text)for__use_two_region_memory
startup_stm32f103xe.o(HEAP) refers (Special)toheapauxi.o(.text)for__use_two_region_memory
startup_stm32f103xe.o(RESET) refers (Special)toheapauxi.o(.text)for__use_two_region_memory
startup_stm32f103xe.o(RESET) referstostartup_stm32f103xe.o(STACK)for__initial_sp
==============================================================================
RemovingUnusedinputsectionsfromthe image.
Removing main.o(.rev16_text), (4bytes).
Removing main.o(.revsh_text), (4bytes).
Removing main.o(.rrx_text), (6bytes).
Removing gpio.o(.rev16_text), (4bytes).
Removing gpio.o(.revsh_text), (4bytes).
384unusedsection(s) (total34104bytes) removedfromthe image.
==============================================================================
Image SymbolTable
LocalSymbols
SymbolNameValueOvTypeSizeObject(Section)
../Core/Src/gpio.c0x00000000Number0gpio.oABSOLUTE
../Core/Src/main.c0x00000000Number0main.oABSOLUTE
../Core/Src/stm32f1xx_hal_msp.c0x00000000Number0stm32f1xx_hal_msp.oABSOLUTE
../Core/Src/stm32f1xx_it.c0x00000000Number0stm32f1xx_it.oABSOLUTE
../Core/Src/system_stm32f1xx.c0x00000000Number0system_stm32f1xx.oABSOLUTE
../Core/Src/tim.c0x00000000Number0tim.oABSOLUTE
../Core/Src/usart.c0x00000000Number0usart.oABSOLUTE
../Drivers/STM32F1xx_HAL_Driver/Src/stm32f1xx_hal.c0x00000000Number0stm32f1xx_hal.oABSOLUTE
../Drivers/STM32F1xx_HAL_Driver/Src/stm32f1xx_hal_cortex.c0x00000000Number0stm32f1xx_hal_cortex.oABSOLUTE
../Drivers/STM32F1xx_HAL_Driver/Src/stm32f1xx_hal_dma.c0x00000000Number0stm32f1xx_hal_dma.oABSOLUTE
../Drivers/STM32F1xx_HAL_Driver/Src/stm32f1xx_hal_exti.c0x00000000Number0stm32f1xx_hal_exti.oABSOLUTE
GlobalSymbols
SymbolNameValueOvTypeSizeObject(Section)
BuildAttributes$THM_ISAv4$P$D$K$B$S$PE$A:L22UL41UL21$X:L11$S22US41US21$IEEE1$IW$USESV6$~STKCKD$USESV7$~SHL$OSPACE$ROPI$EBA8$UX$STANDARDLIB$REQ8$PRES8$EABIv20x00000000Number0anon$obj.oABSOLUTE
__ARM_use_no_argv0x00000000Number0main.oABSOLUTE
__ARM_exceptions_init - Undefined WeakReference
__alloca_initialize - Undefined WeakReference
__arm_preinit_ - Undefined WeakReference
__cpp_initialize__aeabi_ - Undefined WeakReference
_terminate_alloc - Undefined WeakReference
_terminate_user_alloc - Undefined WeakReference
_terminateio - Undefined WeakReference
__Vectors_Size0x00000130Number0startup_stm32f103xe.oABSOLUTE
__Vectors0x08000000Data4startup_stm32f103xe.o(RESET)
__Vectors_End0x08000130Data0startup_stm32f103xe.o(RESET)
__main0x08000131Thumb Code8__main.o(!!!main)
in0x2000001cData4main.o(.data)
uin0x20000020Data4main.o(.data)
uwTick0x20000024Data4stm32f1xx_hal.o(.data)
uwTickPrio0x20000028Data4stm32f1xx_hal.o(.data)
uwTickFreq0x2000002cData1stm32f1xx_hal.o(.data)
==============================================================================
MemoryMapofthe image
Image Entry point :0x08000131
LoadRegion LR_IROM1 (Base:0x08000000,Size:0x00002de8,Max:0x00080000,ABSOLUTE, COMPRESSED[0x00002da8])
Execution Region ER_IROM1 (Exec base:0x08000000,Loadbase:0x08000000,Size:0x00002b94,Max:0x00080000,ABSOLUTE)
Exec AddrLoadAddrSizeTypeAttrIdx ESectionNameObject
0x080000000x080000000x00000130DataRO3RESETstartup_stm32f103xe.o
0x080001300x080001300x00000008Code RO2955* !!!mainc_w.l(__main.o)
0x080001380x080001380x00000034Code RO3143!!!scatter c_w.l(__scatter.o)
0x0800016c0x0800016c0x0000003aCode RO3141!!dczerorl c_w.l(__dczerorl.o)
0x080001a60x080001a60x00000002PAD
0x080001a80x080001a80x0000001cCode RO3145!!handler_zi c_w.l(__scatter_zi.o)
Execution Region RW_IRAM1 (Exec base:0x20000000,Loadbase:0x08002b94,Size:0x00008bb0,Max:0x00010000,ABSOLUTE, COMPRESSED[0x00000214])
Exec AddrLoadAddrSizeTypeAttrIdx ESectionNameObject
0x20000000COMPRESSED0x00000010DataRW18.data test.o
0x20000010COMPRESSED0x00000014DataRW78.data main.o
0x20000024COMPRESSED0x00000009DataRW1481.data stm32f1xx_hal.o
0x2000002dCOMPRESSED0x00000003PAD
0x20000030COMPRESSED0x00000004DataRW2832.data system_stm32f1xx.o
0x20000034COMPRESSED0x00000004PAD
0x20000038COMPRESSED0x0000021cDataRW2910.data debug_revice.o
==============================================================================
Image componentsizes
Code (inc.data) RODataRWDataZIDataDebugObjectName
17260003002debug_function.o
58098054021043763debug_revice.o
364000767gpio.o
2882402050486558main.o
6426304032768820startup_stm32f103xe.o
152320905977stm32f1xx_hal.o
3042200029503stm32f1xx_hal_cortex.o
510100001927stm32f1xx_hal_dma.o
832400002092stm32f1xx_hal_gpio.o
848000918stm32f1xx_hal_msp.o
17841100006112stm32f1xx_hal_rcc.o
1260440009974stm32f1xx_hal_tim.o
160220002453stm32f1xx_hal_tim_ex.o
18441000011460stm32f1xx_hal_uart.o
66120004980stm32f1xx_it.o
2024401155system_stm32f1xx.o
1341001606385test.o
1921800721702tim.o
2202600681778usart.o
----------------------------------------------------------------------
870252236259635068581326ObjectTotals
0032000(incl.Generated)
1802760(incl. Padding)
----------------------------------------------------------------------
Code (inc.data) RODataRWDataZIDataDebugLibraryMemberName
5800000__dczerorl.o
8000068__main.o
000000__rtentry.o
1200000__rtentry2.o
600000__rtentry4.o
5280000__scatter.o
2800000__scatter_zi.o
18000080exit.o
60000152heapauxi.o
000000indicate_semi.o
200000libinit.o
200000libinit2.o
200000libshutdown.o
200000libshutdown2.o
84009668libspace.o
78000080rt_memclr_w.o
200000rtexit.o
1000000rtexit2.o
12400068sys_exit.o
74000080sys_stackheap_outer.o
2000068use_no_semi.o
80416000272daddsub_clz.o
90400092dfixu.o
156400092dnaninf.o
12000068dretinf.o
4308000168faddsub_clz.o
62400084ffixu.o
140400084fnaninf.o
10000068fretinf.o
000000usenofp.o
----------------------------------------------------------------------
20925600961592LibraryTotals
600000(incl. Padding)
----------------------------------------------------------------------
Code (inc.data) RODataRWDataZIDataDebugLibraryName
382160096664c_w.l
170440000928fz_ws.l
----------------------------------------------------------------------
20925600961592LibraryTotals
----------------------------------------------------------------------
==============================================================================
Code (inc.data) RODataRWDataZIDataDebug
1079457836259635164577922Grand Totals
1079457836253235164577922ELF Image Totals (compressed)
1079457836253200ROM Totals
==============================================================================
Total ROSize(Code + ROData)11156(10.89kB)
Total RWSize(RWData+ ZIData)35760(34.92kB)
Total ROMSize(Code + ROData+ RWData)11688(11.41kB)
仔细观察可以发现.map文件主要由以下几个部分组成:
Component: ARM Compiler 5.06update6(build750) Tool: armlink [4d35ed]
==============================================================================
SectionCrossReferences
==============================================================================
RemovingUnusedinputsectionsfromthe image.
==============================================================================
Image SymbolTable
LocalSymbols
SymbolNameValueOvTypeSizeObject(Section)
GlobalSymbols
SymbolNameValueOvTypeSizeObject(Section)
==============================================================================
MemoryMapofthe image
==============================================================================
Image componentsizes
==============================================================================
Code (inc.data) RODataRWDataZIDataDebug
==============================================================================
Total ROSize(Code + ROData)
Total RWSize(RWData+ ZIData)
Total ROMSize(Code + ROData+ RWData)
而我们最关注的信息如函数和全局变量的地址与大小都在.map文件中的Image Symbol Table->Global Symbols。知道了这些地址,我们只需将其感兴趣的函数与变量地址发送给单片机,单片机通过指针就可以执行相应的函数了。整个上位机正是基于这个原理而编写的。具体流程如下图所示:

2.2 class Get_Map_Address_And_Size_Table的实现——————.map中函数和全局变量的地址与大小等信息提取
函数和全局变量的地址与大小都在.map文件中的Image Symbol Table->Global Symbols,由Symbol Name、Value、Ov Type、Size、Object(Section)组成,所以先定义一个public struct Symbol来包含上述信息:
publicstructSymbol{publicString Symbol_Name;publicuintSymbol_Address;publicSymbolType Symbol_Type;publicushortSymbol_Size;publicString Symbol_Section;};
接下来就是通过FileStream获取.map文件中的信息,并定位至Image Symbol Table->Global Symbols,读取Symbol Name、Value、Ov Type、Size、Object(Section)并赋值给symbol_table:
publicvoidCreate_Address_And_Size_Table(String filename){try{
uinti;
FileStream file_read =newFileStream(filename, FileMode.Open, FileAccess.Read);//新建文件流
filelist = File.ReadAllLines(filename, Encoding.Default);//读取文件内容所有行保存到字符串数组中。
for(i =0; i <= filelist.Length -1; i++)//定位到感兴趣的位置
{
if(filelist[i].Contains("Global Symbols"))
{
break;
}
}
for(uintj = i; j <= filelist.Length -1; j++)
{
if(filelist[j].Contains("Object(Section)"))
{
i = j +1;
break;
}
}
if(i < filelist.Length -1)//获取信息
{
//Table_DeInit();
Get_Symbol_Data(i);
}
file_read.Close();
}
catch(Exception ex)
{
MessageBox.Show(ex.Message);
}
}
Get_Symbol_Data(i);就是负责将Image Symbol Table->Global Symbols中的Symbol Name、Value、Ov Type、Size、Object(Section)赋值给symbol_table。有两点需要说明一下:
1)由于在Global Symbols中,
xxxxxx - Undefined Weak Reference
不包含有用信息,是需要被排除的,可以通过Contains("- Undefined Weak Reference")方法将其排除。
2)Image Symbol Table->Global Symbols中的Symbol Name、Value、Ov Type、Size、Object(Section)是通过空格将数据进行分割,所以可以通过
Split(newChar[] {' '}, StringSplitOptions.RemoveEmptyEntries);就可以得到数据集。
void Get_Symbol_Data(uint index)函数如下:
privatevoidGet_Symbol_Data(uintindex)
{
table_length =0;
while(index <= filelist.Length -1)
{
if(filelist[index].Equals(""))
{
index++;
continue;
}
if(filelist[index].Contains("=") ==false)
{
if(filelist[index].Contains("- Undefined Weak Reference"))//排除- Undefined Weak Reference
{
index++;
continue;
}
else
{
intstr_index =0;
string[] split_str = filelist[index].Split(newChar[] {' '}, StringSplitOptions.RemoveEmptyEntries);//获取数据集
symbol_table[table_length].Symbol_Name = split_str[str_index];
str_index++;
symbol_table[table_length].Symbol_Address = Convert.ToUInt32(split_str[str_index],16);
str_index++;
if(split_str[str_index].Equals("Thumb"))
{
symbol_table[table_length].Symbol_Type = SymbolType.Thumb_Code;
}
elseif(split_str[str_index].Equals("Section"))
{
symbol_table[table_length].Symbol_Type = SymbolType.Section;
}
elseif(split_str[str_index].Equals("Number"))
{
symbol_table[table_length].Symbol_Type = SymbolType.Number;
}
elseif(split_str[str_index].Equals("Data"))
{
symbol_table[table_length].Symbol_Type = SymbolType.Data;
}
str_index++;
if(split_str[str_index].Equals("Code"))
{
str_index++;
symbol_table[table_length].Symbol_Size = Convert.ToUInt16(split_str[str_index],10);
str_index++;
symbol_table[table_length].Symbol_Section = split_str[str_index];
}
else
{
symbol_table[table_length].Symbol_Size = Convert.ToUInt16(split_str[str_index],10);
str_index++;
symbol_table[table_length].Symbol_Section = split_str[str_index];
}
table_length = table_length +1;
index++;
if(table_length >= table_len)
{
break;
}
}
}
else
{
break;
}
以上是class Get_Map_Address_And_Size_Table最主要的实现方法。通过这两个方法,就可以得到.map文件中函数与全局变量的信息了。
2.3 class Get_Function_Address_And_Size_Table的实现——————获取我们所需的函数列表
在得到含有函数与全局变量的信息的symbol_table后,我们需要得到我们感兴趣的函数列表。在本上位机中,需要用户新建一个.function文件。在该文中包含有用户需要调试的函数列表。一般只需直接复制.h文件中的函数申明即可。然后上位机通过该列表获取函数名称、参数、返回类型等参量,最后在symbol_table中查询该函数,并获取其地址。以上就是class Get_Function_Address_And_Size_Table所要实现的目标。在class Get_Function_Address_And_Size_Table中先定义
publicstructFunction
{
publicStringFunction_List_Name;
publicStringFunction_Name;
publicuint Function_Address;
publicStringFunction_Parameter1;
publicStringFunction_Parameter2;
publicStringFunction_Parameter3;
publicStringFunction_Parameter4;
publicStringFunction_Parameter5;
publicStringFunction_Return;
publicuint Function_Parameter_Number;
};
以方便存储所要调试函数信息。这里需要需要注意的是,由于C#中struct不能像C中struct一样直接定义一个固定长度的数组,所以直接用Function_ParameterX这样的笨办法来定义5个函数参数信息。
在class Get_Function_Address_And_Size_Table中最重要的就是void Get_Need_Function_Table()函数。其获取.function文件中的函数列表并解析处该列表函数名称、参数、返回类型等参量,并赋值给function_table中。
private void Get_Need_Function_Table()
{
uintindex=0;
for(index=0;index< table_length;index++)
{
string[] split_str = filelist[index].Split(new Char[] {' '}, StringSplitOptions.RemoveEmptyEntries);
uint str_index =0;
function_table[index].Function_List_Name = filelist[index];
if(split_str[str_index].Equals("unsigned") || split_str[str_index].Equals("signed")) //Function_Return
{
function_table[index].Function_Return = split_str[str_index] +" "+ split_str[str_index +1];
str_index = str_index +2;
}
else
{
function_table[index].Function_Return = split_str[str_index];
str_index++;
}
if(split_str[str_index].Equals("*"))
{
function_table[index].Function_Return = function_table[index].Function_Return + split_str[str_index];
str_index++;
}
if(split_str[str_index].Contains("*")) //Function_Name
{
function_table[index].Function_Return = function_table[index].Function_Return +"*";
function_table[index].Function_Name = split_str[str_index].TrimStart(new char[1] {'*'});
str_index++;
}
else
{
function_table[index].Function_Name = split_str[str_index];
}
string[] split_paramenter_str = new String[3];
split_paramenter_str = function_table[index].Function_Name.Split(new Char[] {'('}, StringSplitOptions.RemoveEmptyEntries);
function_table[index].Function_Name = split_paramenter_str[0];
string[] paramenter = filelist[index].Split(new Char[] {'('}, StringSplitOptions.RemoveEmptyEntries);//Function_Parameter_Number
String paramenter_string = paramenter[1];
paramenter_string = paramenter_string.TrimEnd(new char[2] {')',';'});
str_index =0;
if(paramenter_string.Equals("") || paramenter_string.Equals(" ") || paramenter_string.Equals("void"))
{
function_table[index].Function_Parameter_Number =0;
function_table[index].Function_Parameter1 ="";
function_table[index].Function_Parameter2 ="";
function_table[index].Function_Parameter3 ="";
function_table[index].Function_Parameter4 ="";
function_table[index].Function_Parameter5 ="";
}
elseif(paramenter_string.Contains(","))
{
string[]s= paramenter_string.Split(new Char[] {','}, StringSplitOptions.RemoveEmptyEntries);
switch(s.Length)
{
case2: function_table[index].Function_Parameter1 = Get_Data_Kind(s[0]);
function_table[index].Function_Parameter2 = Get_Data_Kind(s[1]);
function_table[index].Function_Parameter3 ="";
function_table[index].Function_Parameter4 ="";
function_table[index].Function_Parameter5 ="";
function_table[index].Function_Parameter_Number =2;
break;
case3: function_table[index].Function_Parameter1 = Get_Data_Kind(s[0]);
function_table[index].Function_Parameter2 = Get_Data_Kind(s[1]);
function_table[index].Function_Parameter3 = Get_Data_Kind(s[2]);
function_table[index].Function_Parameter4 ="";
function_table[index].Function_Parameter5 ="";
function_table[index].Function_Parameter_Number =3;
break;
case4: function_table[index].Function_Parameter1 = Get_Data_Kind(s[0]);
function_table[index].Function_Parameter2 = Get_Data_Kind(s[1]);
function_table[index].Function_Parameter3 = Get_Data_Kind(s[2]);
function_table[index].Function_Parameter4 = Get_Data_Kind(s[3]);
function_table[index].Function_Parameter5 ="";
function_table[index].Function_Parameter_Number =4;
break;
case5: function_table[index].Function_Parameter1 = Get_Data_Kind(s[0]);
function_table[index].Function_Parameter2 = Get_Data_Kind(s[1]);
function_table[index].Function_Parameter3 = Get_Data_Kind(s[2]);
function_table[index].Function_Parameter4 = Get_Data_Kind(s[3]);
function_table[index].Function_Parameter5 = Get_Data_Kind(s[4]);
function_table[index].Function_Parameter_Number =8;
break;
}
}
else
{
function_table[index].Function_Parameter_Number =1;
function_table[index].Function_Parameter1 = Get_Data_Kind(paramenter_string);
function_table[index].Function_Parameter2 ="";
function_table[index].Function_Parameter3 ="";
function_table[index].Function_Parameter4 ="";
function_table[index].Function_Parameter5 ="";
}
}
在得到function_table列表后,只需通过
for(uint i =0; i <function_table.table_length; i++)
{
index = map_table.Get_Index(function_table.function_table[i].Function_Name);
addr=map_table.Get_Address(index);
function_table.Set_Address(i, addr);
}
用以实现存储全局变量的相关信息。
2.5 控制说明
2.5.1 命令字及其数据格式
函数发送命令字:
函数返回值命令字:
下位机接收超时命令字:
有人会疑惑STM32的地址只有4字节,为何在命令字中地址却占用8字节?这要从不同类型数据转换为byte说起。
将不同类型数据的函数参数转换为byte的技巧就是使用联合体。只要在联合体中定义不同类型的变量与最大字长的char数组,就可以很容易的得到其在内存中的分布。在一开始函数参数转换时,为了兼容double类型函数参数,在“联合体”中定义了double,导致其长度为8字节。而函数地址转换也使用了这一方法,所以发送命令字中地址长度也变为8字节。需要注意的是,在C#中没有联合体这一概念,所以只能使用struct并指定变量起始地址以实现C的联合体:
publicstructTypeUnion
{
[FieldOffset(0)]
publicbyteuc;
[FieldOffset(0)]
publicsbytesc;
[FieldOffset(0)]
publicushortus;
[FieldOffset(0)]
publicshortss;
[FieldOffset(0)]
publicuintui;
[FieldOffset(0)]
publicuintpointer;//指针
[FieldOffset(0)]
publicintsi;
[FieldOffset(0)]
publicfloatf;
[FieldOffset(0)]
publicdoubled;
}
由于不能定义char[8],所以之后还要使用static byte[] StructToBytes(object structObj)得到相应变量的内存分布byte[8]
2.5.2 调试函数与全局变量的发送流程
按下函数调试发送按钮之后,会触发void SendFunctionButton_Click(object sender, EventArgs e)函数。在该函数中主要流程是判断串口是否开启->函数参数类型转换->CRC校验->超时判断与重发。函数参数类型转换主要由TypeUnion TypeTransfer(String type_s,String text_s)完成。该函数主要依据参数类型,将传入的参数用 Convert.ToXXX(text_s, f_base)方法转换为对应的数据,并直接赋值给TypeUnion,即一个联合体变量,然后通过static byte[] StructToBytes(object structObj)得到内存分布byte[8]。
而CRC校验则使用CRC16 CITT算法。在前49个字节填充完毕后,最后两个字节先赋值为0,做一次CRC校验,得到的数据再赋值给最后两个字节。
2.5.3 函数返回值接收流程函数
在发送完函数调试命令后,上位机会自动等待直至接收到下位机发送的回复或到达设置的超时时间。利用static object BytesToStuct(byte[] bytes, Type type)将前8个字节转换为TypeUnion变量。而CRC校验则使用CRC16 CITT算法。在前8个字节填充完毕后做一次CRC校验。如果校验失败则直接做一次超时处理,并在一定时间后重新发送函数调试命令。
2.5.4 超时与重传处理
在实际的串口数据收发中,难免会遇到数据收发丢失或中断。比如这次开发中使用虚拟串口收发数据就遇到数据丢失的情况:



明明监控数据都正确收发,但就是会漏数据,也不知怎么回事。没办法,只能做超时重发处理以应对这种情况。在上位机中,主要通过函数bool Is_Timeout()来处理这一情况。
privateboolIs_Timeout()
{
booltimeout =false;
ushortcount_ =0;
while(SerialPort.BytesToRead < RETURN_MAX_LENTH)
{
System.Threading.Thread.Sleep(1);//每隔1ms读取数据是否都收到
count_++;
if(count_ > timeout_set)
{
break;
}
}
if(count_ < timeout_set)//未超时数据处理
{
byte[] byteArray =newbyte[RETURN_MAX_LENTH];
SerialPort.Read(byteArray,0, byteArray.Length);
uintcount =0;
for(uinti =0; i < PARAMENT_MAX_LENTH; i++)//收到8个字节都是0xFF,说明下位机未正确收到数据
{
if(byteArray[i] ==0xFF)
{
count++;
}
}
if(count >= PARAMENT_MAX_LENTH)
{
timeout =true;
}
else
{
if(function_send)//获取函数返回值
{
function_send =false;
ushortcrc1 =0;
crc1 = (ushort)byteArray[RETURN_MAX_LENTH -2];
crc1 = (ushort)(crc1 <<8);
crc1 = (ushort)(crc1 | (ushort)byteArray[RETURN_MAX_LENTH -1]);
byte[] byte_Array =newbyte[RETURN_MAX_LENTH -2];
for(uinti =0; i < RETURN_MAX_LENTH -2; i++)
{
byte_Array[i] = byteArray[i];
}
CRC16 c =newCRC16();
ushortcrc = c.GetCRC16(byte_Array);
if(crc == crc1)
{
TypeUnion return_data = (TypeUnion)BytesToStuct(byte_Array,typeof(TypeUnion));
String s = TypeTransferToString(function_table.function_table[select_function_index].Function_Return, return_data);
RecivedTextBox.Text = s;
}
else
{
timeout =true;
}
}
}
}
else//超过设置的超时时间,直接关闭串口并报错
{
timeout =true;
SerialPort.Close();
ControlSerialButton.Text ="打开串口";
COMComboBox.Enabled =true;
BaudRateComboBox.Enabled =true;
ParityBitsComboBox.Enabled =true;
StopBitComboBox.Enabled =true;
DataBitsComboBox.Enabled =true;
MessageBox.Show("通讯超时!已关闭串口!");
}
returntimeout;}
三、下位机的处理
3.1 接收处理
本来打算使用DMA+空闲中断接收命令字,但考虑到有些低端的单片机没有空闲中断,同时实际使用中出现数据丢失会造成持续的等待,所以直接使用单字节中断接收的方案。在接收到固定的字节后,标志位data_recived置一,并将数据拷贝出来。
voidHAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)//接收中断{
unsignedchari =0;
HAL_TIM_Base_Stop_IT(&htim3);
__HAL_TIM_SET_COUNTER(&htim3,0);
data[data_length] = recv_data;
data_length++;
if(data_length < MAX_RECIVE_LENGTH)
{
HAL_TIM_Base_Start_IT(&htim3);
}
else
{
data_length =0;
data_recived =1;
for(i =0;i < MAX_RECIVE_LENGTH;i++)
{
r_data[i] = data[i];
}
}
HAL_UART_Receive_IT(&huart1, &recv_data,1);
}
3.2 超时处理
由于在实际的数据收发中,会出现数据丢失而造成上位机发送完毕但下位机并未全部接受,从而下位机一直处于等待的情况。为了解决这一情况,引入一个定时为200Hz的定时器。在进入接收中断后,先关闭清空定时器,读取接收的数据后再开启定时。如果出现数据丢失而造成下位机等待的情况,则会引发定时中断。在定时中断内直接清空接收计数器,并给上位机发送超时指令。
voidHAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)//超时中断,超时时间为5ms,一旦超时就发送8字节0xFF{
unsignedchari =0;
HAL_TIM_Base_Stop_IT(&htim3);
__HAL_TIM_SET_COUNTER(&htim3,0);
data_length =0;
for(i =0;i < PARAMENT_MAX_LENTH;i++)
{
returndata[i] =0xFF;
}
crc = crc16(returndata, PARAMENT_MAX_LENTH);
returndata[PARAMENT_MAX_LENTH] = crc >>8;
returndata[PARAMENT_MAX_LENTH +1] = crc;
HAL_UART_Transmit(&huart1, returndata,10,0xFF);
}
3.3 函数指针
通过周期调用void Recived_Command_Handle(void)来实现上位机发送的函数调试命令字。
voidRecived_Command_Handle(void){
unsignedchari =0;
unsignedcharj =0;
if(data_recived)
{
data_recived =0;
crc_16 = (r_data[49] <<8) | r_data[50];
r_data[MAX_RECIVE_LENGTH -2] =0;
r_data[MAX_RECIVE_LENGTH -1] =0;
crc = crc16(r_data, MAX_RECIVE_LENGTH);//CRC_CITT校验
if(crc == crc_16)
{
num = r_data[0];//获取参数数量
for(i =0;i < PARAMENT_MAX_LENTH;i++)//获取地址
{
addr.u_char[i] = r_data[1+ i];
}
for(i =0;i <5;i++)//获取参数
{
for(j =0;j < PARAMENT_MAX_LENTH;j++)
{
paramen[i].u_char[j] = r_data[(1+ PARAMENT_MAX_LENTH) + PARAMENT_MAX_LENTH*i + j];
}
}
if(addr.ul !=0)//获取返回值
{
for(i =0;i < PARAMENT_MAX_LENTH;i++)
{
return_data.u_char[i] =0;
}
return_data = function(addr.ul,num,paramen);
}
for(i =0;i < PARAMENT_MAX_LENTH;i++)
{
returndata[i] = return_data.u_char[i];
}
crc = crc16(return_data.u_char, PARAMENT_MAX_LENTH);
returndata[PARAMENT_MAX_LENTH] = crc >>8;
returndata[PARAMENT_MAX_LENTH +1] = crc;
HAL_UART_Transmit(&huart1, returndata,10,0xFF);
}
}
}
其中函数实现由parameter_kind_union function(unsigned int function_addr,unsigned char paramenter_num,parameter_kind_union *paramenter)完成。
parameter_kind_unionfunction(unsignedintfunction_addr,unsignedcharparamenter_num,parameter_kind_union *paramenter){
void*p = (void*)function_addr;
parameter_kind_union return_data;
switch(paramenter_num)
{
case0: return_data.ull = (*(unsignedint(*)())p)();
break;
case1: return_data.ull = (*(unsignedint(*)())p)(PARAMENT_TRANSFER(paramenter[0]));
break;
case2: return_data.ull = (*(unsignedint(*)())p)(PARAMENT_TRANSFER(paramenter[0]),PARAMENT_TRANSFER(paramenter[1]));
break;
case3: return_data.ull = (*(unsignedint(*)())p) (PARAMENT_TRANSFER(paramenter[0]),PARAMENT_TRANSFER(paramenter[1]),PARAMENT_TRANSFER(paramenter[2]));
break;
case4: return_data.ull = (*(unsignedint(*)())p)(PARAMENT_TRANSFER(paramenter[0]),PARAMENT_TRANSFER(paramenter[1]),PARAMENT_TRANSFER(paramenter[2]),PARAMENT_TRANSFER(paramenter[3]));
break;
case5: return_data.ull = (*(unsignedint(*)())p)(PARAMENT_TRANSFER(paramenter[0]),PARAMENT_TRANSFER(paramenter[1]),PARAMENT_TRANSFER(paramenter[2]),PARAMENT_TRANSFER(paramenter[3]),PARAMENT_TRANSFER(paramenter[4]));
break;
}
returnreturn_data;
}
#define PARAMENT_TRANSFER(p) (*(volatile unsignedint*)((unsignedint)&p))
其操作含义如下:
1)&p含义为取变量p地址;
2)(unsigned int)&p)含义为将取得的地址强制转换为unsigned int;
3)(volatile unsigned int*)((unsigned int)&p)含义为将数字转换为unsigned int类型的指针;
4)*(volatile unsigned int*)((unsigned int)&p)含义为取得该地址内的数据;
可这样会造成一个问题,这就是对于double和long类型的变量,其在取值时会造成错误:


可以看到对于double类型,函数参数值只获得了前4个字节的数据,后4个字节数据丢失了。尝试定义
#define PARAMENT_TRANSFER(p) (*(volatile unsigned long long*)((unsignedint)&p))
可以正确获得double参数,但char等类型则不能正确获取:


所以暂时使用第一种PARAMENT_TRANSFER定义。
(*(unsigned int(*)())p)()则为执行函数,类似于回调函数。通过它可以执行指定的函数。
3.4 修改全局变量
通过void Set_Global_Data(unsigned int addr,unsigned char len,parameter_kind_union data)实现数据的写入。而数组的写入则是循环调用该函数,并加入测试重传功能。
新闻来源:嵌入式大杂烩,文中所述为作者独立观点,不代表icspec立场。更多精彩资讯请下载icspec App。如对本稿件有异议,请联系微信客服specltkj。
暂无评论哦,快来评论一下吧!
2026-07-01

2026-06-30

2026-07-22