}
}
}
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中先定义
public struct Function
{
public String Function_List_Name;
public String Function_Name;
public uint Function_Address;
public String Function_Parameter1;
public String Function_Parameter2;
public String Function_Parameter3;
public String Function_Parameter4;
public String Function_Parameter5;
public String Function_Return;
public uint 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()
{
uint index = 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 = '';
}
else if(paramenter_string.Contains(','))
{
string[] s = paramenter_string.Split(new Char[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
switch(s.Length)
{
case 2: 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;
case 3: 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;
case 4: 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;
case 5: 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的联合体:
public struct TypeUnion
{
[FieldOffset(0)]
public byte uc;
[FieldOffset(0)]
public sbyte sc;
[FieldOffset(0)]
public ushort us;
[FieldOffset(0)]
public short ss;
[FieldOffset(0)]
public uint ui;
[FieldOffset(0)]
public uint pointer; //指针
[FieldOffset(0)]
public int si;
[FieldOffset(0)]
public float f;
[FieldOffset(0)]
public double d;
}
由于不能定义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()来处理这一情况。
private bool Is_Timeout()
{
bool timeout = false;
ushort count_ = 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 = new byte[RETURN_MAX_LENTH];
SerialPort.Read(byteArray, 0, byteArray.Length);
uint count = 0;
for (uint i = 0; i < PARAMENT_MAX_LENTH; i++) //收到8个字节都是0xFF,说明下位机未正确收到数据
上一篇:MMC中断的特点及解决方案
下一篇:SAR ADC内部结构
推荐阅读最新更新时间:2024-11-14 09:32
设计资源 培训 开发板 精华推荐
- LT8570EMS8E-1 1MHz 反相转换器的典型应用从 12V 输入产生 -48V 输出
- LTC2862AHS8-1 故障安全 0 应用的典型应用(空闲状态 = 逻辑 0)
- LTC3890HUH 高效 8.5V 双相降压转换器的典型应用电路
- LTC3707-SYNC 的典型应用 - 高效率、两相同步降压型开关稳压器
- ADP7156ARDZ-3.0-R7 3V 输出电压、1.2A、超低噪声、高 PSRR、RF 线性稳压器的典型应用
- 灯板1102
- 1.9 GHz 低噪声放大器提供 +14 dBm 输入截点
- 【郑州工商学院】红外小车+180506080136
- 8*8RGB点阵
- 【瑞萨】喝水提醒器