在线观看www成人影院-在线观看www日本免费网站-在线观看www视频-在线观看操-欧美18在线-欧美1级

0
  • 聊天消息
  • 系統(tǒng)消息
  • 評論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線課程
  • 觀看技術(shù)視頻
  • 寫文章/發(fā)帖/加入社區(qū)
會員中心
創(chuàng)作中心

完善資料讓更多小伙伴認識你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

一個單片機調(diào)試小工具的編程思路

GReq_mcu168 ? 來源:21ic論壇 ? 作者:紀國圣 ? 2022-05-16 14:35 ? 次閱讀

一、概述

在平時編寫STM32單片機代碼時,我們經(jīng)常會遇到某一個函數(shù)或某一個變量需要反復(fù)調(diào)試的情況,而常用的方法只能是在源碼修改并下載至單片機調(diào)試。反復(fù)這樣不僅麻煩,而且反復(fù)燒寫單片機對其FLASH也有影響,因此就考慮編寫一款小工具,可以實現(xiàn): 1)通過串口控制單片機執(zhí)行我們期望的函數(shù),同時函數(shù)參數(shù)最大支持5個,其參數(shù)類型支持char、short、int、float及其無符號類型和相應(yīng)的指針,不支持long及double。2)對于含有對字符串及數(shù)組操作的函數(shù),需要通過數(shù)組傳值后,在調(diào)用函數(shù)時寫入該變量地址才能實現(xiàn)對這些變量的操作。支持函數(shù)返回值得顯示。3)支持對全局變量進行任意的修改。4)支持十進制與十六進制切換.5)通訊超時自動重傳或關(guān)閉串口。建議配合KEIL一起使用,效果更好。本軟件使用C#編寫,運行環(huán)境為NET 4.5。先讓大家看看效果,感興趣的話可以繼續(xù)往下看: 1.上位機調(diào)試設(shè)置 e3db53a4-d4df-11ec-bce3-dac502259ad0.png ?2.函數(shù)調(diào)用 e3fd9432-d4df-11ec-bce3-dac502259ad0.png ?3.全局變量的寫入 e4106620-d4df-11ec-bce3-dac502259ad0.png ?4.通訊超時處理 e431e50c-d4df-11ec-bce3-dac502259ad0.png ?二、上位機的處理 2.1 原理 在使用keil編譯STM32后,我們會在.hex文件的同一個文件夾中發(fā)現(xiàn)一個.map文件。這個.map文件包含了源碼中函數(shù)與全局變量的地址、大小、優(yōu)化等信息。這里貼一個簡化的.map文件給大家看一下:
Component: ARM Compiler 5.06 update 6 (build 750) Tool: armlink [4d35ed]


==============================================================================


Section Cross References


  startup_stm32f103xe.o(STACK) refers (Special) to heapauxi.o(.text) for __use_two_region_memory
  startup_stm32f103xe.o(HEAP) refers (Special) to heapauxi.o(.text) for __use_two_region_memory
  startup_stm32f103xe.o(RESET) refers (Special) to heapauxi.o(.text) for __use_two_region_memory
  startup_stm32f103xe.o(RESET) refers to startup_stm32f103xe.o(STACK) for __initial_sp




==============================================================================


Removing Unused input sections from the image.


  Removing main.o(.rev16_text), (4 bytes).
  Removing main.o(.revsh_text), (4 bytes).
  Removing main.o(.rrx_text), (6 bytes).
  Removing gpio.o(.rev16_text), (4 bytes).
  Removing gpio.o(.revsh_text), (4 bytes).


384 unused section(s) (total 34104 bytes) removed from the image.


==============================================================================


Image Symbol Table


  Local Symbols


  Symbol Name          Value Ov Type  SizeObject(Section)


  ../Core/Src/gpio.c       0x00000000 Number   0gpio.o ABSOLUTE
  ../Core/Src/main.c       0x00000000 Number   0main.o ABSOLUTE
  ../Core/Src/stm32f1xx_hal_msp.c    0x00000000 Number   0stm32f1xx_hal_msp.o ABSOLUTE
  ../Core/Src/stm32f1xx_it.c     0x00000000 Number   0stm32f1xx_it.o ABSOLUTE
  ../Core/Src/system_stm32f1xx.c   0x00000000 Number   0system_stm32f1xx.o ABSOLUTE
  ../Core/Src/tim.c        0x00000000 Number   0tim.o ABSOLUTE
  ../Core/Src/usart.c        0x00000000 Number   0usart.o ABSOLUTE
  ../Drivers/STM32F1xx_HAL_Driver/Src/stm32f1xx_hal.c 0x00000000 Number   0stm32f1xx_hal.o ABSOLUTE
  ../Drivers/STM32F1xx_HAL_Driver/Src/stm32f1xx_hal_cortex.c 0x00000000 Number   0stm32f1xx_hal_cortex.o ABSOLUTE
  ../Drivers/STM32F1xx_HAL_Driver/Src/stm32f1xx_hal_dma.c 0x00000000 Number   0stm32f1xx_hal_dma.o ABSOLUTE
  ../Drivers/STM32F1xx_HAL_Driver/Src/stm32f1xx_hal_exti.c 0x00000000 Number   0stm32f1xx_hal_exti.o ABSOLUTE


  Global Symbols


  Symbol Name          Value Ov Type  SizeObject(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$EABIv2 0x00000000 Number   0anon$obj.o ABSOLUTE
  __ARM_use_no_argv        0x00000000 Number   0main.o ABSOLUTE
  __ARM_exceptions_init       - Undefined Weak Reference
  __alloca_initialize       - Undefined Weak Reference
  __arm_preinit_          - Undefined Weak Reference
  __cpp_initialize__aeabi_      - Undefined Weak Reference
  _terminate_alloc        - Undefined Weak Reference
  _terminate_user_alloc       - Undefined Weak Reference
  _terminateio          - Undefined Weak Reference
  __Vectors_Size         0x00000130 Number   0startup_stm32f103xe.o ABSOLUTE
  __Vectors          0x08000000 Data   4startup_stm32f103xe.o(RESET)
  __Vectors_End          0x08000130 Data   0startup_stm32f103xe.o(RESET)
  __main           0x08000131 Thumb Code 8__main.o(!!!main)
  in             0x2000001c Data   4main.o(.data)
  uin            0x20000020 Data   4main.o(.data)
  uwTick           0x20000024 Data   4stm32f1xx_hal.o(.data)
  uwTickPrio           0x20000028 Data   4stm32f1xx_hal.o(.data)
  uwTickFreq           0x2000002c Data   1stm32f1xx_hal.o(.data)






==============================================================================


Memory Map of the image


Image Entry point : 0x08000131


Load Region LR_IROM1 (Base: 0x08000000, Size: 0x00002de8, Max: 0x00080000, ABSOLUTE, COMPRESSED[0x00002da8])


  Execution Region ER_IROM1 (Exec base: 0x08000000, Load base: 0x08000000, Size: 0x00002b94, Max: 0x00080000, ABSOLUTE)


  Exec Addr  Load Addr  Size   Type Attr  Idx  E Section Name  Object


  0x08000000 0x08000000 0x00000130 Data RO    3  RESET     startup_stm32f103xe.o
  0x08000130 0x08000130 0x00000008 Code RO   2955* !!!main     c_w.l(__main.o)
  0x08000138 0x08000138 0x00000034 Code RO   3143  !!!scatter    c_w.l(__scatter.o)
  0x0800016c 0x0800016c 0x0000003a Code RO   3141  !!dczerorl    c_w.l(__dczerorl.o)
  0x080001a6 0x080001a6 0x00000002 PAD
  0x080001a8 0x080001a8 0x0000001c Code RO   3145  !!handler_zi  c_w.l(__scatter_zi.o)




  Execution Region RW_IRAM1 (Exec base: 0x20000000, Load base: 0x08002b94, Size: 0x00008bb0, Max: 0x00010000, ABSOLUTE, COMPRESSED[0x00000214])


  Exec Addr  Load Addr  Size   Type Attr  Idx  E Section Name  Object


  0x20000000 COMPRESSED 0x00000010 Data RW   18  .data     test.o
  0x20000010 COMPRESSED 0x00000014 Data RW   78  .data     main.o
  0x20000024 COMPRESSED 0x00000009 Data RW   1481  .data     stm32f1xx_hal.o
  0x2000002d COMPRESSED 0x00000003 PAD
  0x20000030 COMPRESSED 0x00000004 Data RW   2832  .data     system_stm32f1xx.o
  0x20000034 COMPRESSED 0x00000004 PAD
  0x20000038 COMPRESSED 0x0000021c Data RW   2910  .data     debug_revice.o




==============================================================================


Image component sizes




  Code (inc. data) RO Data  RW Data  ZI Data  Debug Object Name


   172    6    0    0    0   3002 debug_function.o
   580   98    0  540   2104   3763 debug_revice.o
  36    4    0    0    0  767 gpio.o
   288   24    0   20   50 486558 main.o
  64   26  304    0  32768  820 startup_stm32f103xe.o
   152   32    0    9    0   5977 stm32f1xx_hal.o
   304   22    0    0    0  29503 stm32f1xx_hal_cortex.o
   510   10    0    0    0   1927 stm32f1xx_hal_dma.o
   832   40    0    0    0   2092 stm32f1xx_hal_gpio.o
  84    8    0    0    0  918 stm32f1xx_hal_msp.o
  1784  110    0    0    0   6112 stm32f1xx_hal_rcc.o
  1260   44    0    0    0   9974 stm32f1xx_hal_tim.o
   160   22    0    0    0   2453 stm32f1xx_hal_tim_ex.o
  1844   10    0    0    0  11460 stm32f1xx_hal_uart.o
  66   12    0    0    0   4980 stm32f1xx_it.o
   2    0   24    4    0   1155 system_stm32f1xx.o
   134   10    0   16    0   6385 test.o
   192   18    0    0   72   1702 tim.o
   220   26    0    0   68   1778 usart.o


  ----------------------------------------------------------------------
  8702  522  362  596  35068 581326 Object Totals
   0    0   32    0    0    0 (incl. Generated)
  18    0    2    7    6    0 (incl. Padding)


  ----------------------------------------------------------------------


  Code (inc. data) RO Data  RW Data  ZI Data  Debug Library Member Name


  58    0    0    0    0    0 __dczerorl.o
   8    0    0    0    0   68 __main.o
   0    0    0    0    0    0 __rtentry.o
  12    0    0    0    0    0 __rtentry2.o
   6    0    0    0    0    0 __rtentry4.o
  52    8    0    0    0    0 __scatter.o
  28    0    0    0    0    0 __scatter_zi.o
  18    0    0    0    0   80 exit.o
   6    0    0    0    0  152 heapauxi.o
   0    0    0    0    0    0 indicate_semi.o
   2    0    0    0    0    0 libinit.o
   2    0    0    0    0    0 libinit2.o
   2    0    0    0    0    0 libshutdown.o
   2    0    0    0    0    0 libshutdown2.o
   8    4    0    0   96   68 libspace.o
  78    0    0    0    0   80 rt_memclr_w.o
   2    0    0    0    0    0 rtexit.o
  10    0    0    0    0    0 rtexit2.o
  12    4    0    0    0   68 sys_exit.o
  74    0    0    0    0   80 sys_stackheap_outer.o
   2    0    0    0    0   68 use_no_semi.o
   804   16    0    0    0  272 daddsub_clz.o
  90    4    0    0    0   92 dfixu.o
   156    4    0    0    0   92 dnaninf.o
  12    0    0    0    0   68 dretinf.o
   430    8    0    0    0  168 faddsub_clz.o
  62    4    0    0    0   84 ffixu.o
   140    4    0    0    0   84 fnaninf.o
  10    0    0    0    0   68 fretinf.o
   0    0    0    0    0    0 usenofp.o


  ----------------------------------------------------------------------
  2092   56    0    0   96   1592 Library Totals
   6    0    0    0    0    0 (incl. Padding)


  ----------------------------------------------------------------------


  Code (inc. data) RO Data  RW Data  ZI Data  Debug Library Name


   382   16    0    0   96  664 c_w.l
  1704   40    0    0    0  928 fz_ws.l


  ----------------------------------------------------------------------
  2092   56    0    0   96   1592 Library Totals


  ----------------------------------------------------------------------


==============================================================================




  Code (inc. data) RO Data  RW Data  ZI Data  Debug 


 10794  578  362  596  35164 577922 Grand Totals
 10794  578  362  532  35164 577922 ELF Image Totals (compressed)
 10794  578  362  532    0    0 ROM Totals


==============================================================================


  Total ROSize (Code + RO Data)      11156 (10.89kB)
  Total RWSize (RW Data + ZI Data)     35760 (34.92kB)
  Total ROM Size (Code + RO Data + RW Data)  11688 (11.41kB)

仔細觀察可以發(fā)現(xiàn).map文件主要由以下幾個部分組成:


Component: ARM Compiler 5.06 update 6 (build 750) Tool: armlink [4d35ed]


==============================================================================


Section Cross References


==============================================================================


Removing Unused input sections from the image.


==============================================================================


Image Symbol Table


  Local Symbols


  Symbol Name          Value Ov Type  SizeObject(Section)
  
  


  Global Symbols


  Symbol Name          Value Ov Type  SizeObject(Section)


  


==============================================================================


Memory Map of the image




==============================================================================


Image component sizes






==============================================================================




  Code (inc. data) RO Data  RW Data  ZI Data  Debug 




==============================================================================


  Total ROSize (Code + RO Data)      
  Total RWSize (RW Data + ZI Data)     
TotalROMSize(Code+ROData+RWData)

而我們最關(guān)注的信息如函數(shù)和全局變量的地址與大小都在.map文件中的Image Symbol Table->Global Symbols。知道了這些地址,我們只需將其感興趣的函數(shù)與變量地址發(fā)送給單片機,單片機通過指針就可以執(zhí)行相應(yīng)的函數(shù)了。整個上位機正是基于這個原理而編寫的。具體流程如下圖所示:

e44a2ea0-d4df-11ec-bce3-dac502259ad0.png

2.2 class Get_Map_Address_And_Size_Table的實現(xiàn)——————.map中函數(shù)和全局變量的地址與大小等信息提取

函數(shù)和全局變量的地址與大小都在.map文件中的Image Symbol Table->Global Symbols,由Symbol Name、Value、Ov Type、Size、Object(Section)組成,所以先定義一個public struct Symbol來包含上述信息:


public struct Symbol
{
 public String Symbol_Name;
 public uint Symbol_Address;
 public SymbolType Symbol_Type;
 public ushort Symbol_Size;
 public String Symbol_Section;
 };

接下來就是通過FileStream獲取.map文件中的信息,并定位至Image Symbol Table->Global Symbols,讀取Symbol Name、Value、Ov Type、Size、Object(Section)并賦值給symbol_table:


public void Create_Address_And_Size_Table(String filename){
  try
  {
  uint i;


  FileStream file_read = new FileStream(filename, FileMode.Open, FileAccess.Read);//新建文件流


  filelist = File.ReadAllLines(filename, Encoding.Default);//讀取文件內(nèi)容所有行保存到字符串?dāng)?shù)組中。


  for (i = 0; i <= filelist.Length - 1; i++)    //定位到感興趣的位置
  {
     if (filelist[i].Contains("Global Symbols"))
    {
     break;
    }
   }


  for (uint j = 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);就是負責(zé)將Image Symbol Table->Global Symbols中的Symbol Name、Value、Ov Type、Size、Object(Section)賦值給symbol_table。有兩點需要說明一下:

1)由于在Global Symbols中,

xxxxxx-UndefinedWeakReference

不包含有用信息,是需要被排除的,可以通過Contains("- Undefined Weak Reference")方法將其排除。

2)Image Symbol Table->Global Symbols中的Symbol Name、Value、Ov Type、Size、Object(Section)是通過空格將數(shù)據(jù)進行分割,所以可以通過

Split(newChar[]{''},StringSplitOptions.RemoveEmptyEntries);

就可以得到數(shù)據(jù)集。

void Get_Symbol_Data(uint index)函數(shù)如下:


private void Get_Symbol_Data(uint index)
{
 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
    {
      int str_index = 0;
      string[] split_str = filelist[index].Split(new Char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);//獲取數(shù)據(jù)集
        
      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;
      }
     else if (split_str[str_index].Equals("Section"))
    {
       symbol_table[table_length].Symbol_Type = SymbolType.Section;
    }
    else if (split_str[str_index].Equals("Number"))
    {
      symbol_table[table_length].Symbol_Type = SymbolType.Number;
     }
    else if (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最主要的實現(xiàn)方法。通過這兩個方法,就可以得到.map文件中函數(shù)與全局變量的信息了。

2.3 class Get_Function_Address_And_Size_Table的實現(xiàn)——————獲取我們所需的函數(shù)列表

在得到含有函數(shù)與全局變量的信息的symbol_table后,我們需要得到我們感興趣的函數(shù)列表。在本上位機中,需要用戶新建一個.function文件。在該文中包含有用戶需要調(diào)試的函數(shù)列表。一般只需直接復(fù)制.h文件中的函數(shù)申明即可。然后上位機通過該列表獲取函數(shù)名稱、參數(shù)、返回類型等參量,最后在symbol_table中查詢該函數(shù),并獲取其地址。以上就是class Get_Function_Address_And_Size_Table所要實現(xiàn)的目標。在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;
};

以方便存儲所要調(diào)試函數(shù)信息。這里需要需要注意的是,由于C#中struct不能像C中struct一樣直接定義一個固定長度的數(shù)組,所以直接用Function_ParameterX這樣的笨辦法來定義5個函數(shù)參數(shù)信息。

在class Get_Function_Address_And_Size_Table中最重要的就是void Get_Need_Function_Table()函數(shù)。其獲取.function文件中的函數(shù)列表并解析處該列表函數(shù)名稱、參數(shù)、返回類型等參量,并賦值給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);
}

用以實現(xiàn)存儲全局變量的相關(guān)信息。

2.5 控制說明

2.5.1 命令字及其數(shù)據(jù)格式

函數(shù)發(fā)送命令字:

函數(shù)返回值命令字:

下位機接收超時命令字:

有人會疑惑STM32的地址只有4字節(jié),為何在命令字中地址卻占用8字節(jié)?這要從不同類型數(shù)據(jù)轉(zhuǎn)換為byte說起。

將不同類型數(shù)據(jù)的函數(shù)參數(shù)轉(zhuǎn)換為byte的技巧就是使用聯(lián)合體。只要在聯(lián)合體中定義不同類型的變量與最大字長的char數(shù)組,就可以很容易的得到其在內(nèi)存中的分布。在一開始函數(shù)參數(shù)轉(zhuǎn)換時,為了兼容double類型函數(shù)參數(shù),在“聯(lián)合體”中定義了double,導(dǎo)致其長度為8字節(jié)。而函數(shù)地址轉(zhuǎn)換也使用了這一方法,所以發(fā)送命令字中地址長度也變?yōu)?字節(jié)。需要注意的是,在C#中沒有聯(lián)合體這一概念,所以只能使用struct并指定變量起始地址以實現(xiàn)C的聯(lián)合體:

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)得到相應(yīng)變量的內(nèi)存分布byte[8]

2.5.2 調(diào)試函數(shù)與全局變量的發(fā)送流程

按下函數(shù)調(diào)試發(fā)送按鈕之后,會觸發(fā)void SendFunctionButton_Click(object sender, EventArgs e)函數(shù)。在該函數(shù)中主要流程是判斷串口是否開啟->函數(shù)參數(shù)類型轉(zhuǎn)換->CRC校驗->超時判斷與重發(fā)。函數(shù)參數(shù)類型轉(zhuǎn)換主要由TypeUnion TypeTransfer(String type_s,String text_s)完成。該函數(shù)主要依據(jù)參數(shù)類型,將傳入的參數(shù)用 Convert.ToXXX(text_s, f_base)方法轉(zhuǎn)換為對應(yīng)的數(shù)據(jù),并直接賦值給TypeUnion,即一個聯(lián)合體變量,然后通過static byte[] StructToBytes(object structObj)得到內(nèi)存分布byte[8]。

而CRC校驗則使用CRC16 CITT算法。在前49個字節(jié)填充完畢后,最后兩個字節(jié)先賦值為0,做一次CRC校驗,得到的數(shù)據(jù)再賦值給最后兩個字節(jié)。

2.5.3 函數(shù)返回值接收流程函數(shù)

在發(fā)送完函數(shù)調(diào)試命令后,上位機會自動等待直至接收到下位機發(fā)送的回復(fù)或到達設(shè)置的超時時間。利用static object BytesToStuct(byte[] bytes, Type type)將前8個字節(jié)轉(zhuǎn)換為TypeUnion變量。而CRC校驗則使用CRC16 CITT算法。在前8個字節(jié)填充完畢后做一次CRC校驗。如果校驗失敗則直接做一次超時處理,并在一定時間后重新發(fā)送函數(shù)調(diào)試命令。

2.5.4 超時與重傳處理

在實際的串口數(shù)據(jù)收發(fā)中,難免會遇到數(shù)據(jù)收發(fā)丟失或中斷。比如這次開發(fā)中使用虛擬串口收發(fā)數(shù)據(jù)就遇到數(shù)據(jù)丟失的情況:

e494a552-d4df-11ec-bce3-dac502259ad0.png

e4b16192-d4df-11ec-bce3-dac502259ad0.png

e4e1e2ea-d4df-11ec-bce3-dac502259ad0.png

明明監(jiān)控數(shù)據(jù)都正確收發(fā),但就是會漏數(shù)據(jù),也不知怎么回事。沒辦法,只能做超時重發(fā)處理以應(yīng)對這種情況。在上位機中,主要通過函數(shù)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讀取數(shù)據(jù)是否都收到
  count_++;
  if (count_ > timeout_set)
 {
  break;
 }
 }


if (count_ < timeout_set)? ?? ?? ?? ?? ?? ?? ?? ?? ?? ? //未超時數(shù)據(jù)處理
 {
 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個字節(jié)都是0xFF,說明下位機未正確收到數(shù)據(jù)
  {
   if (byteArray[i] == 0xFF)
  {
    count++;
  }
  }
  if (count >= PARAMENT_MAX_LENTH)
  {
  timeout = true;
 }
 else
  {
   if (function_send)              //獲取函數(shù)返回值
  {
    function_send = false;


    ushort crc1 = 0;
    crc1 = (ushort)byteArray[RETURN_MAX_LENTH - 2];
    crc1 = (ushort)(crc1 << 8);
    crc1 = (ushort)(crc1 | (ushort)byteArray[RETURN_MAX_LENTH - 1]);


    byte[] byte_Array = new byte[RETURN_MAX_LENTH - 2];
    for (uint i = 0; i < RETURN_MAX_LENTH - 2; i++)
   {
     byte_Array[i] = byteArray[i];
   }
   CRC16 c = new CRC16();
   ushort crc = 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                  //超過設(shè)置的超時時間,直接關(guān)閉串口并報錯
{
 timeout = true;


  SerialPort.Close();
  ControlSerialButton.Text = "打開串口";
  COMComboBox.Enabled = true;
  BaudRateComboBox.Enabled = true;
  ParityBitsComboBox.Enabled = true;
  StopBitComboBox.Enabled = true;
  DataBitsComboBox.Enabled = true;
      
   MessageBox.Show("通訊超時!已關(guān)閉串口!");
  }


    return timeout;}

三、下位機的處理

3.1 接收處理

本來打算使用DMA+空閑中斷接收命令字,但考慮到有些低端的單片機沒有空閑中斷,同時實際使用中出現(xiàn)數(shù)據(jù)丟失會造成持續(xù)的等待,所以直接使用單字節(jié)中斷接收的方案。在接收到固定的字節(jié)后,標志位data_recived置一,并將數(shù)據(jù)拷貝出來。


void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)                //接收中斷{
  unsigned char i = 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 超時處理

由于在實際的數(shù)據(jù)收發(fā)中,會出現(xiàn)數(shù)據(jù)丟失而造成上位機發(fā)送完畢但下位機并未全部接受,從而下位機一直處于等待的情況。為了解決這一情況,引入一個定時為200Hz的定時器。在進入接收中斷后,先關(guān)閉清空定時器,讀取接收的數(shù)據(jù)后再開啟定時。如果出現(xiàn)數(shù)據(jù)丟失而造成下位機等待的情況,則會引發(fā)定時中斷。在定時中斷內(nèi)直接清空接收計數(shù)器,并給上位機發(fā)送超時指令。



void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)                                                                        //超時中斷,超時時間為5ms,一旦超時就發(fā)送8字節(jié)0xFF{
      unsigned char i = 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 函數(shù)指針

通過周期調(diào)用void Recived_Command_Handle(void)來實現(xiàn)上位機發(fā)送的函數(shù)調(diào)試命令字。


void Recived_Command_Handle(void){
  unsigned char i = 0;
  unsigned char j = 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];                                                                                                  //獲取參數(shù)數(shù)量
        
     for(i = 0;i < PARAMENT_MAX_LENTH;i++)? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?//獲取地址
     {
      addr.u_char[i] = r_data[1 + i];
      }
          
     for(i = 0;i < 5;i++)                                                                                                //獲取參數(shù)
    {
       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);                                
    }
   }
}

其中函數(shù)實現(xiàn)由parameter_kind_union function(unsigned int function_addr,unsigned char paramenter_num,parameter_kind_union *paramenter)完成。


parameter_kind_union function(unsigned int function_addr,unsigned char paramenter_num,parameter_kind_union *paramenter){
  void *p = (void *)function_addr;
  parameter_kind_union return_data;
  
  switch(paramenter_num)
 {
   case 0: return_data.ull = (*(unsigned int(*)())p)();
      break;
   case 1: return_data.ull = (*(unsigned int(*)())p)(PARAMENT_TRANSFER(paramenter[0]));
      break;
   case 2: return_data.ull = (*(unsigned int(*)())p)(PARAMENT_TRANSFER(paramenter[0]),PARAMENT_TRANSFER(paramenter[1]));
      break;
   case 3: return_data.ull = (*(unsigned int(*)())p)  (PARAMENT_TRANSFER(paramenter[0]),PARAMENT_TRANSFER(paramenter[1]),PARAMENT_TRANSFER(paramenter[2]));
       break;
    case 4: return_data.ull = (*(unsigned int(*)())p)(PARAMENT_TRANSFER(paramenter[0]),PARAMENT_TRANSFER(paramenter[1]),PARAMENT_TRANSFER(paramenter[2]),PARAMENT_TRANSFER(paramenter[3]));
        break;
   case 5: return_data.ull = (*(unsigned int(*)())p)(PARAMENT_TRANSFER(paramenter[0]),PARAMENT_TRANSFER(paramenter[1]),PARAMENT_TRANSFER(paramenter[2]),PARAMENT_TRANSFER(paramenter[3]),PARAMENT_TRANSFER(paramenter[4]));
       break;
   }
  
   return return_data;
}

#definePARAMENT_TRANSFER(p)(*(volatileunsignedint*)((unsignedint)&p))

其操作含義如下:

1)&p含義為取變量p地址;

2)(unsigned int)&p)含義為將取得的地址強制轉(zhuǎn)換為unsigned int;

3)(volatile unsigned int*)((unsigned int)&p)含義為將數(shù)字轉(zhuǎn)換為unsigned int類型的指針;

4)*(volatile unsigned int*)((unsigned int)&p)含義為取得該地址內(nèi)的數(shù)據(jù);

可這樣會造成一個問題,這就是對于double和long類型的變量,其在取值時會造成錯誤:

e5200ef8-d4df-11ec-bce3-dac502259ad0.png

e55e9204-d4df-11ec-bce3-dac502259ad0.png

可以看到對于double類型,函數(shù)參數(shù)值只獲得了前4個字節(jié)的數(shù)據(jù),后4個字節(jié)數(shù)據(jù)丟失了。嘗試定義

#definePARAMENT_TRANSFER(p)(*(volatileunsignedlonglong*)((unsignedint)&p))

可以正確獲得double參數(shù),但char等類型則不能正確獲?。?/p>

e579def6-d4df-11ec-bce3-dac502259ad0.png

e5e2f9f4-d4df-11ec-bce3-dac502259ad0.png

所以暫時使用第一種PARAMENT_TRANSFER定義。

(*(unsigned int(*)())p)()則為執(zhí)行函數(shù),類似于回調(diào)函數(shù)。通過它可以執(zhí)行指定的函數(shù)。

3.4 修改全局變量

通過void Set_Global_Data(unsigned int addr,unsigned char len,parameter_kind_union data)實現(xiàn)數(shù)據(jù)的寫入。而數(shù)組的寫入則是循環(huán)調(diào)用該函數(shù),并加入測試重傳功能。

審核編輯 :李倩

聲明:本文內(nèi)容及配圖由入駐作者撰寫或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問題,請聯(lián)系本站處理。 舉報投訴
  • 單片機
    +關(guān)注

    關(guān)注

    6064

    文章

    44931

    瀏覽量

    647471
  • STM32
    +關(guān)注

    關(guān)注

    2290

    文章

    11017

    瀏覽量

    362647
  • 函數(shù)
    +關(guān)注

    關(guān)注

    3

    文章

    4372

    瀏覽量

    64283

原文標題:自編一個單片機調(diào)試小工具,并談?wù)勂渚幊趟悸?/p>

文章出處:【微信號:mcu168,微信公眾號:硬件攻城獅】歡迎添加關(guān)注!文章轉(zhuǎn)載請注明出處。

收藏 人收藏

    評論

    相關(guān)推薦
    熱點推薦

    單片機c語言編程實例大全

    單片機c語言編程實例大全_18
    發(fā)表于 04-30 16:11 ?2次下載

    STM32F10xxx單片機編程手冊

    電子發(fā)燒友網(wǎng)站提供《STM32F10xxx單片機編程手冊.pdf》資料免費下載
    發(fā)表于 04-14 14:56 ?8次下載

    單片機Debug工具性能對比 單片機調(diào)試常用命令

    單片機(Microcontroller Unit, MCU)調(diào)試是嵌入式開發(fā)中的重要環(huán)節(jié),它幫助開發(fā)者發(fā)現(xiàn)和修復(fù)代碼中的錯誤,優(yōu)化程序性能。不同的
    的頭像 發(fā)表于 12-19 09:56 ?1285次閱讀

    單片機Debug與仿真區(qū)別

    單片機的開發(fā)是復(fù)雜的過程,涉及到硬件設(shè)計、軟件開發(fā)和測試等多個環(huán)節(jié)。為了確保單片機能夠按照預(yù)期工作,開發(fā)者需要使用Debug和仿真技術(shù)來檢測和修正代碼中的錯誤。 Debug(
    的頭像 發(fā)表于 12-19 09:47 ?832次閱讀

    單片機編程語言有哪些選擇

    單片機(Microcontroller Unit,MCU)編程是指為單片機編寫程序的過程,這些程序控制單片機的行為和功能。單片機廣泛應(yīng)用于嵌
    的頭像 發(fā)表于 11-01 14:13 ?2201次閱讀

    單片機調(diào)試常見問題與解決方法

    單片機調(diào)試是嵌入式系統(tǒng)開發(fā)中的重要環(huán)節(jié),它涉及到對單片機程序的測試和優(yōu)化,以確保系統(tǒng)能夠正常工作。在
    的頭像 發(fā)表于 11-01 14:11 ?2789次閱讀

    在DRA7xx器件上使用CONFIG-FS的USB復(fù)合小工具

    電子發(fā)燒友網(wǎng)站提供《在DRA7xx器件上使用CONFIG-FS的USB復(fù)合小工具.pdf》資料免費下載
    發(fā)表于 10-10 09:26 ?0次下載
    在DRA7xx器件上使用CONFIG-FS的USB復(fù)合<b class='flag-5'>小工具</b>

    8位單片機為何不會被高端單片機取代?

    、SPI和I2C)。 (5)開發(fā)工具:有多種開發(fā)工具可用于8位單片機,包括集成開發(fā)環(huán)境(IDE)、編譯器、調(diào)試器和編程
    發(fā)表于 09-24 16:51

    單片機基本io功能調(diào)試過程

    單片機基本IO功能的調(diào)試過程涉及多個步驟,旨在確保IO口能夠正確地執(zhí)行輸入和輸出操作。以下是調(diào)試過程,涵蓋了從準備階段到實際測試的關(guān)鍵步
    的頭像 發(fā)表于 09-14 14:38 ?1328次閱讀

    單片機WiFi模塊怎樣連接手機APP

    連接 :將WiFi模塊與單片機物理連接。 編程單片機 :編寫代碼使單片機能通過WiFi模塊與互聯(lián)網(wǎng)通信。 創(chuàng)建服務(wù)器 :在單片機上創(chuàng)建
    的頭像 發(fā)表于 09-10 15:31 ?3349次閱讀

    keil可以讀出單片機的程序嗎

    表述存在定的誤解,因為Keil主要是用于編寫、編譯和調(diào)試單片機程序的工具,而不是直接從單片機中讀取已
    的頭像 發(fā)表于 09-02 10:32 ?2016次閱讀

    stm32單片機用什么軟件編程

    STM32單片機種廣泛應(yīng)用于嵌入式系統(tǒng)領(lǐng)域的微控制器,具有高性能、低功耗、豐富的外設(shè)接口等特點。要對STM32單片機進行編程,需要選擇合適的軟件
    的頭像 發(fā)表于 09-02 10:16 ?3191次閱讀

    單片機圖形化編程軟件有哪些

    單片機圖形化編程軟件為開發(fā)者提供了種更加直觀、易于上手的編程方式,尤其適合初學(xué)者和教育領(lǐng)域。以下是些常見的
    的頭像 發(fā)表于 09-02 10:14 ?3346次閱讀

    暑假如何學(xué)習(xí)單片機

    暑假是學(xué)習(xí)和掌握單片機基礎(chǔ)知識的良好時機。以下是關(guān)于如何在暑假期間學(xué)習(xí)單片機的建議計劃
    的頭像 發(fā)表于 07-03 09:19 ?850次閱讀
    <b class='flag-5'>一</b><b class='flag-5'>個</b>暑假如何學(xué)習(xí)<b class='flag-5'>單片機</b>

    cadence實用腳本工具分享,實現(xiàn)orcad原理圖快捷設(shè)計,減少重復(fù)性工作

    本文會教大家如何配置這樣的工具,并且分享我正在用的小工具,
    的頭像 發(fā)表于 06-15 17:31 ?8700次閱讀
    cadence實用腳本<b class='flag-5'>工具</b>分享,實現(xiàn)orcad原理圖快捷設(shè)計,減少重復(fù)性工作
    主站蜘蛛池模板: 一级做a爰片久久毛片免费看 | 国产精品视频久久久 | 伊人久久影视 | 中文字幕天天躁夜夜狠狠综合 | 曰本福利写真片视频在线 | 日本一区视频在线播放 | 色噜噜狠狠狠狠色综合久 | 国产69精品久久久久9牛牛 | 成人精品视频在线观看播放 | 中文字幕一区2区3区 | a毛片免费观看完整 | 秋霞特色大片18入口私人高清 | 天天做天天爱天天一爽一毛片 | 啪啪免费看 | 一级不卡毛片 | 5月丁香6月婷婷 | 性videofree极品另类 | 亚洲美女激情视频 | 久久久久久噜噜噜久久久精品 | 色天天躁夜夜躁天干天干 | 天堂资源网| 一级黄色毛片免费看 | 男女交性视频免费 | 四虎在线最新永久免费 | 亚洲综合激情丁香六月 | 午夜在线观看免费视频 | 老熟女一级毛片 | 日本黄色的视频 | 欧美性一区| 欧美一级片网址 | 日本黄色片www | 亚洲视频区 | 综合色婷婷| 艹逼视频免费 | 91大神视频在线播放 | 久久观看午夜精品 | 你懂得的在线观看免费视频 | 日日夜夜天天久久 | 午夜寂寞在线一级观看免费 | 日韩毛片高清免费 | 欧美一级片网址 |