2023年11月29日 星期三

for .net core mvc ,關於json輸出、反序列化一二事

 1.有時系統太自動也不好,像是在json轉換時就是最好的例子了
明明在serverside裡的類別屬性是大寫開頭,結果輸出到json ( 像是在Action裡用「return Json(.....) 」) 去就變成小寫開頭
這就是System.Text.Json的問題了
想整個規避掉就直接在Program.cs裡做點小設定
-------------------------.net 6.0的 program.cs--------------------
 builder.Services.AddControllersWithViews()        
 .AddJsonOptions(options => {
     options.JsonSerializerOptions.PropertyNamingPolicy = null; // This preserves the original case
 });
-------------------------------------------------
(一般都只是「 builder.Services.AddControllersWithViews() ;  」而沒有用到AddJsonOptions)


有json出就有json入,最常用的是系統預設的
System.Text.Json.JsonSerializer.Deserialize<T>(string)」
沒有什麼不好啦,只是厚....如果遇到null轉換就會出錯了
如:
-----------------------
System.Text.Json.JsonException: 'The JSON value could not be converted to System.Nullable`1[System.Decimal]
-----------------------
像上述的錯誤會發生在parsing json string 把null轉成其他型態時會發生錯誤
如何解決?
這時就要請老朋友--NewtonSoft的Json lib相助了
-------------------------------------------
var v = Newtonsoft.Json.JsonConvert.DeserializeObject<Type>(jsonfiledString);
-----------------------------------------
問題就解決了
參考這裡

以上,大家加油吧




2023年10月28日 星期六

informix之.net core EF 套用.

 直接參考這文章做就差不多了
很多人會卡在Scaffold-DbContext這一關
重點是要注意一下PORT ,因為informix EF Core用的是DRDA的port,所以不是之前我們用odbc的(預設)9088而是9089.
如同上述內文說的:Please note: The IBM Data Server providers only works with the Distributed Relational Database Architecture™ (DRDA) protocols, and you therefore need to ensure that your Informix server is configured accordingly.


(這點你要跟你們家的DBA問一下給不給連,也要問網管開不開port,說真的,我也沒把握一次要他們從DEV、UAT到Production這樣一次開這麼多東西給你,所以本單元真的就是自己練功用的)


上述超連結提到的package有點舊了,我實驗的package有update,也都正常
-----------project target framework是.net 6.0------------------



-----------------------------
這是我localhost的scaffold-Dbcontext script,留做我日後小抄用
------------------------------------------------------------- 
Scaffold-DbContext -Connection "user id=informix;server=localhost:9089;database=db_with_log;Password=xxxOOO" -Provider IBM.EntityFrameworkCore -OutputDir Models  -Force -UseDatabaseNames -Tables test_user,test_data

--------------------------------------------
彩蛋一下:
這份PDF不錯,如果你是.net+informix的重度使用者,可以看一下

OS:明明用到的機會渺茫.我還是堅持到底地把它試出來玩了幾番,這也是種職業病吧





2023年10月20日 星期五

拜見老前輩--informix使用記錄(.net 4.5用)

最近因為案子需要,所以接觸到informix

為此在本機(win11)安裝了informix develper version
照理說應該不需要管到informix server的管理,這應該是DBA的工作
不過如果是本機開發測試需要,真的都要自己來

註:以下是為ODBC+.net4.5準備的操作,如果是可以用 .net core開發informix應用的話,請用entity framework core即可,IBM有為.net core發行了相關的EFcore套件.比較不必費心在transaction控管上面.既然上面要求要用.net4.x就老老實實配合就是了

----言歸正傳-----
安裝informix develper server不是問題,我們是用Odbc連線做之後的增刪修查
(因為這樣日後移機會比較好改比較簡單,只要做odbc 設定就好)
雖說「比較簡單」,一點也不簡單
首先來說,你要怎麼測能不能連,當然是用他們自家的工具--ConnectTesst Demo 測比較快

(以上的database下拉可以選才是真的連起來了,有驚嘆號沒選項就是沒連好,仔細看他的訊息,別使戇力猛試)



如果連不上,或是想把上面的host改成ip的話,請到「C:\Program Files\IBM Informix Software Bundle\etc\sqlhosts.ol_informix1410」去改把host pc name改成 ip

--------------------
ol_informix1410 olsoctcp 127.0.0.1 ol_informix1410 
dr_informix1410 drsoctcp 127.0.0.1 dr_informix1410 
lo_informix1410 olsoctcp 127.0.0.1 lo_informix1410 
--------------------
必要時,連「C:\Program Files\IBM Informix Software Bundle\ol_informix1410.cmd」的內容也要改
(連在C:\Windows\System32\drivers\etc\裡的「hosts」也加上本機名稱跟ip對應也再所不惜地改吧)
--------------------

set INFORMIXDIR=C:\PROGRA~1\IBMINF~1
rem set REGMACHINE=\\你的主機名稱    <--先remark起來,以下用ip替代
set REGMACHINE=127.0.0.1
set INFORMIXSERVER=ol_informix1410
set ONCONFIG=onconfig.ol_informix1410
set INFORMIXSQLHOSTS=C:\Program Files\IBM Informix Software Bundle\etc\sqlhosts.ol_informix1410
set GL_USEGLU=1
set PATH=%INFORMIXDIR%\bin;%PATH%
--------------------
這關過了,之後就是ODBC設定了(基本上,「ConnectTest Demo」 能通的話,這也是可以通了)
「可是只能用英文,我要能支援中文的話,可以嗎」

當然可以,而且你也一定不會在內建的「sysxxx」db裡操作(DBA也會把你趕出去的),我們要用別的工具來create db.免驚啦,免錢的哦,他就是鼎鼎大名的DBVisualizer 而且重點不是只有在creeate db而已,是在其連線過程.
一般只會講到這一頁


但是,為了要設定之後的locale,就要用到這一頁了
(注意上面有三個user setting:DB_LOCALE,CLIENT_LOCALE & charSet,一般是沒這三個設定)

這樣後,用DBvisualizer連入informix後,就可以用它來create db了



然後,再回到odbc設定語系,才不會因為語系不同而無法連入
(照理dba會幫你做好db的建置的,但別忘了註明我要中文在地化支援)
看一下中文語系是不是DB的預設語系


這樣你就可以好好享用ODBC + .net操作了
「可是....我怎麼試,transaction總是auto commit,到底要怎樣才能支援transaction?」
「很好,這表示你真的有在玩下去,你可以出山下山了」
想要用.net的OdbcTransaction ,你不只要在odbc那裡把「Auto Commit Optimization」不打勾之外


(以上這點有沒有 check都沒差)
更重要的是.....
在create db時,log mode一定不能選「none」,好歹有個log模式就是了(不知道就用「with log」)


這樣,才會支援交易機制
上述的設定,請參考以下討論
大家加油吧
(忘了跟這次的要角--IBM informix ODBC Driver Setup合影一下,來,笑一個吧)





2023年7月5日 星期三

小試一下,FSMC之於LCD(NT35510),在F103的設定(by Stm32CubeMx)

  雖然用GPIO會比較快,也可以省卻「如果你的chip不支援FSMC...」的問題

不過多學會點會比較好些,也許哪天會用到也不一定,
本篇參考以下兩個URL:這個 & 這個
NT35510的規格書在此

先上圖吧


(別忘了Back Light要On起來,沒有back light,沒有顯示)
先用read ID & color fomat測看看,如果讀出是亂碼,請到上圖的NOR/PSRAME timing for wirte access那裡改一下,有時要多點clock才會正確 (參考上面兩個URL的內文介紹如個推算,因為我是用NT35510的,所以會以第二個link為主)

程式碼如下
---------------------------------------------------
//因為是用base1 第 4區,所以base address是0x6C000000
 (即0x60000000~0x6FFFFFFF分成區,第四區起始位址即為0x6C000000,參考「STM32F103 战舰开发指南V1.1.pdf」P365)
#define FMC_ADDR_CMD  ((uint32_t) 0x6C000000)
//而A10做為C/D(或曰register select),又要往左shfit一位,故為00800
#define FMC_ADDR_DATA  ((uint32_t) 0x6C000800)
//在 MX_FSMC_Init();後,這些對應建立起來後,就可以直接用位址的方式讀寫,不必再用8080介面操作了...
void lcd_wr_cmd(volatile uint16_t cmd)
{
    cmd = cmd;
    *(uint16_t *)(FMC_ADDR_CMD) = cmd;
}
void lcd_wr_data(volatile uint16_t data)
{
     data = data;
*(uint16_t *)(FMC_ADDR_DATA) = data;
}
uint16_t lcd_rd_data(void)
{
volatile uint16_t ram; 
ram = *(uint16_t *)(FMC_ADDR_DATA);
return ram;
}
...
...
main的內容
    HAL_Init();
   MX_FSMC_Init();
   MX_USART1_UART_Init();
 //神說,要有光,於是就有了光
 HAL_GPIO_WritePin(LCD_BL_GPIO_Port,LCD_BL_Pin,GPIO_PIN_SET);
  HAL_Delay(10);
  lcd_wr_cmd(0x1100);    //Sleep out & booster on 
  HAL_Delay(10);
  lcd_wr_cmd(0x2900);   //display memory  to frame.
  HAL_Delay(10);
  //第一步,先試著讀id,如果傳回值不是80h的話,就是錯了
  lcd_wr_cmd(0x0401);
  HAL_Delay(100);
  uint16_t chip_id=0xabcd;
  chip_id=lcd_rd_data();
  
  //試著讀寫 color format,原先是8x8x8 RBG格式,如今我們為了測試,用565格式
  //0x0C00是read color mode,而0x3A00是write color mode.
  uint16_t pix_fomrat_org=0;
  lcd_wr_cmd(0x0c00);pix_fomrat_org=lcd_rd_data();
  lcd_wr_cmd(0x3A00); lcd_wr_data(0x55);
  lcd_wr_cmd(0x0c00);
  uint16_t pix_fomrat=lcd_rd_data();
   //清除資料一下 (預設尺寸是480*800)
  lcd_wr_cmd(0x2c00);; //demo how to do "clear screen"
  for(uint32_t i=0;i<(0x01df-1)*(0x03ef-1);i++){
      lcd_wr_data(0x07E0);
  }
  
  //最後,把大頭照放上去 x,y : 200,200, width& height:140x140
  lcd_wr_cmd(0x2A00);lcd_wr_data(0);;
  lcd_wr_cmd(0x2A01);lcd_wr_data(200);
  lcd_wr_cmd(0x2A02);lcd_wr_data(339>>8);
  lcd_wr_cmd(0x2A03);lcd_wr_data(339&0xff);
  
  lcd_wr_cmd(0x2B00);lcd_wr_data(0);
  lcd_wr_cmd(0x2B01);lcd_wr_data(200);
  lcd_wr_cmd(0x2B02);lcd_wr_data(339>>8);
  lcd_wr_cmd(0x2B03);lcd_wr_data(339&0xff);  
  lcd_wr_cmd(0x2c00); //demo how to do "clear screen"
  for(uint32_t i=0;i<(140*140);i++){
      lcd_wr_data(g_pexl[i]);
  }
  
 //然後就是while(1)...

---------------------------------------------------
 耶~ 有看到圖片了(會不會太自戀了?)
------




------
這次的實作基本上只是個小插曲,還是建議用GPIO直接去操作,以清除螢幕這個函數來說好了
-----------
void fill_color(uint16_t color, uint32_t count)

   //當然以下的HAL_GPIO_WritePin都可以再直接操作BSRR暫存器來最佳化之,
  //只是為了實作驗證是否了解正確,直接用HAL程式庫了
    set_d0tod7_output();
    HAL_GPIO_WritePin(LCD_DC_GPIO_Port,LCD_DC_Pin,GPIO_PIN_RESET);//RS:0
    HAL_GPIO_WritePin(LCD_WRITE_GPIO_Port,LCD_WRITE_Pin,GPIO_PIN_SET);//write 1
    HAL_GPIO_WritePin(LCD_READ_GPIO_Port,LCD_READ_Pin,GPIO_PIN_SET);//RD 1
    HAL_GPIO_WritePin(LCD_CS_GPIO_Port,LCD_CS_Pin,GPIO_PIN_RESET);//CS 0
    
    write_byte_to_gpio(0x2c00);
    HAL_GPIO_WritePin(LCD_WRITE_GPIO_Port,LCD_WRITE_Pin,GPIO_PIN_RESET);//write 0
    HAL_GPIO_WritePin(LCD_WRITE_GPIO_Port,LCD_WRITE_Pin,GPIO_PIN_SET);//write 1
    
    //set to data mode
    HAL_GPIO_WritePin(LCD_DC_GPIO_Port,LCD_DC_Pin,GPIO_PIN_SET);//RS:1
    write_byte_to_gpio(color);
    uint32_t pin16u=(uint32_t)LCD_WRITE_Pin<< 16u;
    for(uint32_t i=0;i<count;i++)
    {
         LCD_WRITE_GPIO_Port->BSRR = pin16u; //RESET
         LCD_WRITE_GPIO_Port->BSRR = LCD_WRITE_Pin;//SET
     }
    //all done
    HAL_GPIO_WritePin(LCD_CS_GPIO_Port,LCD_CS_Pin,GPIO_PIN_SET);//CS 1
    HAL_GPIO_WritePin(LCD_DC_GPIO_Port,LCD_DC_Pin,GPIO_PIN_SET);//RS:1
    HAL_GPIO_WritePin(LCD_READ_GPIO_Port,LCD_READ_Pin,GPIO_PIN_SET);//read 1
    HAL_GPIO_WritePin(LCD_WRITE_GPIO_Port,LCD_WRITE_Pin,GPIO_PIN_SET);//write 1
}
-----------
這比填入FSMC再填入LCD快了點(直接高低電位切換,當然快啊,這樣比很不講武德捏)
而且會看技術規格文件,是這行必備的生存技能
當然FSMC會了也是很好的一件事,畢竟很多時候可以對應到別的設備的,大大節省開發難度
要學的東西真的太多了,一起加油吧.

2023年6月18日 星期日

For Xamarin.android 如何用Android.Hardware.usbconnect來做HID 的write report類似的動作

 最近拿到一個設備做整合的案子,是USB-HID介面
跟廠商要到的sample code是用HidSharp的寫.
-------------------------------
                    var device = HidDevices.Enumerate(vendorId, productId).First();
                    dev.OpenDevice();
                    const UInt32 ZEROS=0x00;
                    const UInt32  Heading04=.....;
                    var bytes=new List<Byte>();
                    bytes.AddRange(BitConverter.GetBytes(Heading04).Reverse().ToArray());
                    bytes.AddRange(BitConverter.GetBytes(ZEROS).Reverse().ToArray());
                    //to write data to HID,use "report" as pocket
                    var outputReport = dev.CreateReport();
                    outputReport.ReportId = 0;
                    outputReport.Data=bytes.ToArray();
                    dev.WriteReport(outputReport);
-------------------------------
今欲轉移到安卓設備上,一直找不到HID 類似的實作.
「既然沒有,就自己包了」,不過HID真的是很不單純的規格(光是USB就有一堆書跟規格要看了),談個容易
「不然就觀察看看上述的code,在USB介面是如何動作的?」
 這時就要借助好用的工具「DeviceMonitor Studio」(試用期14天,動作快!!)
因為我們知道是用USB的HID規格,所以監視方式請用HID等選項


不過,真正比較有用的是在packet view的狀態.....

嗯,看那裡面的參數Request、Value、index、data.... ,真的是「火燒豬頭-- 面熟面熟咧」 
「阿這不就是UsbConnection的ControlTransfer嘛」
「好啊,那你試看看用controlTransfer會不會傳回-1?如果不是-1表示成功了」
先讀看看Descrption看看,如果可能,就可以把其他動作給它(controlTransfer)來做了
xamarin.android ControlTransfer的API在此
「我試了UsbAddressing.In的方式一直讀取不了資料,心很累....」
別一開始就埋頭苦幹用戇力(gōng-lak)--先找成功案例的code來改來測才好比對驗證
先以這篇討論的例子來改
---
UsbDeviceConnection.controlTranfer(0x81, 0x06, 0x2200, 0x00, byte[] buffer, 104, 2000)
----
「第一個參數不是UsbAddressing型態嗎?怎麼會是一個叫0x81的整數?」
「儘信API不如無API,更何況那是從java包裝來的,直接把0x81 cast成UsbAddress就是了」
所以用以下的code來測,結果傳回值不再是-1,而是37(是的buffer 裡也有值了,我出運啊,我出運啊啦...)
-------------------------
var returvalue = conn.ControlTransfer((UsbAddressing)0x81, 0x06, 0x2200, 0,buffer,200,int.MaxValue);
//returnvalue為37
--------------------------
「嗯....又是另一個問題了,,為什麼是0x81,而不是UsbAddress.In? 為什麼用UsbAddress.In & UsbAddress.Out傳回都是-1 ...為什麼」
「請把剛才那篇討論文看完,答案就在大師回覆的連結裡」
參考這段文章吧,為什麼address選0x81? 因為bit7 & bit0是1,表示為Device to host(Interface)
這個bmRequestType表很重要,他是問題核心.


為什麼request用0x06? 因為要讀Description


綜上所知,如果要把我們在DeviceMonitor studio的packet所看到的動作,用controlTransfer來包裝的話
那address要選什麼?.....答案就在畫面裡....


試試看把Request & Value都改成我們要傳送的資料,而UsbAddress改成0b00100001看看
(即表"bmRequestType"裡的"interface" & "class"兩個bit都要on起來)
--------------------------
List<Byte> bytes = new List<Byte>(0);
              ......
                bytes.AddRange(BitConverter.GetBytes(Heading04).Reverse().ToArray());
                bytes.AddRange(BitConverter.GetBytes(ZEROS).ToArray());
                returvalue = await conn.ControlTransferAsync((UsbAddressing)0b00100001, 0x09, 0x0200, 0, bytes.ToArray(), bytes.Count, int.MaxValue);
                System.Console.WriteLine($" return: {returvalue}");
                await Task.Delay(10);
---------------------------
returnvalue不再是-1,也很正確地輸出訊號到HID上了
這次真的很難得的經驗啊
一起加油吧







2023年5月15日 星期一

for STM32F103,抓輸入的PWM的週期及高電位時間

 別看原子哥老師的程式碼寫的落落長,

反正就把握一個原則:
測量器TIM8的slave mode用reset mode (即:一有輸入,即觸發一次reset /update 事件)
TIM8的trigger是TI1FP1 (即,以timer input filter polarity channel1 做為觸源)
但,因為CNT的增減還是由這個timer自動動作,所以還是要有時基,故選內部時鐘
是要做為測量工具,儘可能求最細精度及最大週期,所以就用不分頻及period為65535為基礎,如下圖


至於direct & indirect ,是看pdf 教材來的

(摘錄自「STM32F103 战舰开发指南V1.1.pdf」22.5節  高級定時器PWM輸入模式實驗 323頁)
別隨便用chatGPT的code,那會讓你更迷糊的
至於待測物--PWM訊號源的話,先以TIM3 CHANNEL2為PWM源,跳線PB5<->PC6 (TIM8 channel1的GPIO pin),時鐘的基本設定上,因為考量到TIM1的最長週期為65535個count 約0.0009秒,即900微秒...
「這麼短,如果遇到超過1亳秒的週期豈不是掛了」
「先從小的測,測出來正確再放大」
TIM3的初設定:72-1分頻,ARR為100-1,佔空比為50 
初版程式如下:
------------------------------------------

int main(void)
{
  HAL_Init();
  SystemClock_Config();
  MX_GPIO_Init();
  MX_TIM3_Init();
  MX_TIM8_Init();
  MX_USART1_UART_Init();

   HAL_TIM_PWM_Start(&htim3,TIM_CHANNEL_2);
   HAL_Delay(5000);
   HAL_TIM_Base_Start(&htim8);
   HAL_TIM_IC_Start_IT(&htim8,TIM_CHANNEL_1);
   HAL_TIM_IC_Start_IT(&htim8,TIM_CHANNEL_2);
  
  while (1)
  {
      HAL_Delay(100);
      if(1)
      {
          printf(" period :%i ; hi-time:%i \r\n",period,high_time);
      }
  }
}
void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim)
{
     period = HAL_TIM_ReadCapturedValue(htim, TIM_CHANNEL_1);
     high_time = HAL_TIM_ReadCapturedValue(htim, TIM_CHANNEL_2);
}
------------------------------------------
結果輸出的值是period為7198,hi-time為3598.都要加2才是正確的7200 & 3600
(其中的+1是因為「從0起算」;另一個加一是什麼?知道的朋友請開示一下)
驗算一下:7200* 1/72000000 = 0.0001 sec ,佔空比50%,故為3600 * 1/72000000=0.00005 sec



「那,放大到10ms的週期,要怎麼改?」
「就在TIM8的update事件都加上365536,就這麼簡單」
「可是update事件也可能是因為TIM3 rising edge引發的,也可能是TIM8自己的reset引發的,
如何是好?」
這就好好安排「update 」及「capture compare」兩個中斷的順序了
我們打算先在input capture做一個旗標,確定是真的已經進入了一個偵測流程了(而且要排除在半路遇到下降緣引發的input capture 中斷),日後的update interrupt 再來進行計次,
所以中斷的優先高低是input capture 高於 update 
「一定要這麼複雜嗎?」
「一定要,因為預設是update優於input capture」



然後,code小改一下...
1.main那裡要呼叫「__HAL_TIM_ENABLE_IT(&htim8,TIM_IT_UPDATE);」
(別忘了配置了什麼中斷就要用這方式enable起來)
---------------------------------
uint8_t begin_capture=0;
uint32_t overflow_count=0;
//讓timer8一直count下去吧
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
    if(htim->Instance==TIM8 && begin_capture==1)
    {
        overflow_count++;
    }
}
//加上over flow之後的推算
void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim)
{
   if (htim->Channel == HAL_TIM_ACTIVE_CHANNEL_1 )
   {
      //測得rsing edge,但是否為一個新的測量流程?
       if(begin_capture==0)
       {
           begin_capture=1;
           overflow_count=0;
           return;
       }else{
          //測量完畢
           begin_capture=0;
          //別忘了+2
           period = (HAL_TIM_ReadCapturedValue(htim, TIM_CHANNEL_1)+2)+
                            (overflow_count>0?(65536*(overflow_count-1)):0);
          
       }
       
   }
    //測得falling edge ,而且還是「測量中」才算數
   if (htim->Channel == HAL_TIM_ACTIVE_CHANNEL_2 && begin_capture==1)
   {
       high_time = HAL_TIM_ReadCapturedValue(htim, TIM_CHANNEL_2)+2+
                            (overflow_count>0?(65536*(overflow_count-1)):0);
   }
}
---------------------------------
我們再去把TIM3改成很長的週期:預分頻為7200-1,ARR為50000-1,pulse為2500
程式輸出結果為
period:360000000 & hi-time:18000000 
算一下 360000000*72Mhz=5sec;  18000000 * 72Mhz=0.25秒,

 
正確無誤,
而且也不必像正點原子哥的程式那麼複雜
「為什麼那個overflow要減一才能乘65536?」
「因為第一次的input capture calback後還是執行了update中斷了,所以要把最初的那次扣掉」
(事實上我也是實驗,先修改/扣除...再回想,才了解這一點的,要學的東西太多了)

附記:
STM32 timer系列可以參考這個播放 清單, 這位老師也是座寶山....




2023年4月12日 星期三

for stm32,如果要在GPIO的EXTI中斷處理裡面用delay的話,怎麼設計比較好

或許你會直接想說那好辨,就是在 HAL_GPIO_EXTI_Callback 裡做HAL_Delay
但是這會讓你debug到死都會在HAL_Delay裡完成不了,為什麼?
因為預設的SysTick的優先序是排到15去,而後續定義的EXTI的優先層級都比它還高
當然是SysTick在EXTI call back裡會死在那裡 ,參考這裡說明
所以,要調整一下下兩者間的NVIC優先序,如下圖
-------------------------------------


-------------------------------------
這樣就可以在HAL_GPIO_EXTI_Callback 裡呼叫HAL_Delay了
為什麼SysTick會被安排到這麼後面的優先序裡去?可能是因為分時多工會用到
既然是分時多工之用,那一定是會有時間分割上的出入,自然不若GPIO那樣要即時處理電位起伏帶來的事件/中斷來得緊急(優先)之故,畢竟GPIO是在最前線作戰的單位,其所關注的事件一定是有很高的優先處理順序.這點很合理的
「可是,如果我要delay到小於1毫秒的delay,或是不想去修改它們的優先序,如何是好?」
如果不想用Standard Peripheral Library的用操作SysTick暫存器的寫法的話,可以用Timer對counter增減的方式來做delay的依據
參方這位老師的教學,(prescaler 72-1=  72MHz/72 -> 1MHz--> 1us ) 注意,是微秒級,不是奈秒.
-------------------


--------------------
並在main.c裡加入delay_us函數.(當然你也要有「MX_TIM1_Init();」跟「HAL_TIM_Base_Start(&htim1);」啟用TIM1)
-------------------
void delay_us (uint16_t us)
{
	__HAL_TIM_SET_COUNTER(&htim1,0);  // set the counter value a 0
	while (__HAL_TIM_GET_COUNTER(&htim1) < us);  // wait for the counter to reach the us input in the parameter
}
-------------------
之後,再回到我們之前要處理的「HAL_GPIO_EXTI_Callback 」使用它
------------假設我們設定的是下降緣觸發事件--------------
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{   
       //delay一下,事緩則圓--消除突波之用
        delay_us(1000*50);
        delay_us(1000*50);
        //如果此時按鈕線被拉回高電位的話,不處理
        //密技:即便是用在中斷模式,stm32的GPIO 腳位還是可以讀取狀態的
        if(HAL_GPIO_ReadPin(BUTTON_KEY1_GPIO_Port,BUTTON_KEY1_Pin)==  GPIO_PIN_SET)
        {
          return;
        }
       //是的,已經確定是按下狀態
      //處理中 ...
       ....
------------------------------------------------------------------
以上.
(科班出身的都那麼拚了,那非本科出身的,豈不是要更努力才行了?)

2023年3月30日 星期四

For mac 如何製作開機碟並進入「 Startup Manager」

你或許會說很簡單啊~用distk util就可以了
(是啦,chatGPT也是這樣說啦) 

可是單純在一般操作下用disk util會無法restore的.
真正的restore的動作是要在startup manager mode下用disk util才能做的.
 這時就算是你真的用Ctrl+R或是option或是T長按....都是不行的 
原來是這個startup manager進入畫面機制是隨著版本有差的
相信很多人跟我一樣踢到鐵板,好心的帥哥美女幫我們整理在這裡
如果你是 Apple Silicon Mac 的話,是要用電源鍵長按密技才能完成「奧義啟動」
------------------------------------------------------

Apple Silicon Mac Startup Key Combinations

It is simple for Apple Silicon Mac users to remember the startup key combination because you just need to press the power button or Touch ID.

You can press the power button or Touch ID and release it until the startup options show up on the screen.

What can you do in the Mac startup window? The following tools or options are available:

  • macOS Recovery.
  • System Restore Mac.
  • Install or reinstall macOS.
  • Safari.
  • Disk Utility Mac.
  • Startup Security Utility.
  • Terminal.
  • Startup Disk.
  • -----------------
  • 以上,祝開機大吉
  •   P.S. chatGPT不錯用啦,只是你一定要去驗證監督就是了 (不然公司請它就夠了咩)

同場加映:
如何把Ventura安裝到外接式USB碟上去:
首先,我的USB是2T的,所以切成250G給安裝區Mac OS Extended (Journaled)格式
(假設此區名為UsbBoot)
其他的要設為APFS格式
(假設此區名為UsbMain)
他們的partition type都是GUID Partition Map格式
之後到app store下載ventura,用以下的指令安裝到你的「安裝磁區」
然後在console用以下指令"把安裝程式"置於之前的250G安裝區  ( /Volumns/UsbBoot )
----------------------------------------------
sudo /Applications/Install\ macOS\ Ventura.app/Contents/Resources/createinstallmedia --volume /Volumes/UsbBoot --nointeraction
---------------------------------------------
順利完成後UsbBoot會存放安裝Ventura所需要的工具
然後重開機,長接電源鈕進入start Manager,選擇install ventura
再選擇欲安裝到的目的碟(剛才的UsbMain)
之後就是安裝的事了




2023年2月26日 星期日

在mac之下的vs2022,點擊「*.xib」檔,卻不見xcode開啟設計介面,即便開啟,也無法看到其他相關檔案(如*.h檔)

 如果你要用純粹的xamarin.ios架構設計,而不用xamarin.form架構的話

你應該會覺得在設計介面時(如UIViewController),是非常麻煩的事
終究還是要回到mac裡去作業
這不要緊,最麻煩的事,還有些靈異現象--
「明明昨天設計LaunchScreen還好好的,一點LaunchScreen.storyboard
就會啟動xocde的設計畫面,而且左側會把整個c#的xamarin.ios project目錄結構都列出來
而今天才剛加入一個新的ViewController,點了.xib,卻美釘美當」.....
如圖..
------


------
如果你硬在xcode裡開那個xib檔,左側的navigator project view會不出現整個project
只會出現那個xib檔
----


------
嗯,這很奇怪,在加入這個新的UIViewController之前真的都好好的...
「可是,你有沒有看到,好像多出來一個兩奇奇怪怪的東西.....」
「對耶,那個目錄跟它的外層都有多一個View1.cs」
「拿掉看看,反正死馬當活馬醫了」
「來人啊,把那兩個View1.cs都給我拖進.....乖乖桶回收桶去」
說也奇怪(不是奇怪,是我不了解xcode,畢竟它對我而言,真的只是姑不而將才用的畫面設計工具)
刪了這兩個檔案後,再雙點擊xib檔,就又正常了
------


-------

每幾年ios改版,對app的權限就又會再設限了些.於是我就要再回來改它拯救它
好像終極戰士(praditor)系列一樣,三天兩頭就要回來打怪一次....



2023年2月4日 星期六

用winrar解簡體中文壓縮檔產出成亂碼目錄、檔案名稱!? 如何解決?

 從中國的網站那裡抓下來一些文件壓縮檔,直接在繁體中文作業系統中的winrar 解開,會變成亂碼目錄,結果是看得「霧沙沙」,如何是好 ?

「呃...是要灌一個簡體中文版的windows嗎?」 厚,毋免安爾厚工啦~~~

 拿到簡體命名檔的zip檔時,先別直接解開,先用winRAR來開啟,確定目部檔名是否正確
(即,在該zip檔 mouse right click menu中選「WinRAR-->以WinRA開啟」)
(這還算好的,有的是直接出現幾何符號的,覺得根本是加密過的?)
這時,請在winrar的「選項-->名稱編碼」裡,選取「936 簡體中文GBK」


然後....winrar裡的目錄名稱就正確了



這樣,直接把這個正確名稱的的目錄拖出來,就可以解出正確的目錄了

TIP:
 如果有一堆簡中檔名的zip檔要解的話,不用一直重複上面的動作,直接在winrar裡的「.. 」(回上層目錄),開啟檢視其他要解開的zip檔,這樣就不必一直切換「名稱編碼」,檢視得到正確的檔案目錄名稱便可直接拖出到目的資料夾裡,比較省事

(如果你要用keil開發的話,還是路徑裡不要有中文字元才不會出現打不開project的現象)

一起加油吧


2023年1月12日 星期四

for ROS2 同一個 package 即要有cpp的node,又想加入python node,如何定義這樣的package?

 聰明的你一定是那句:「摻在一起做成撒尿牛丸啊,笨蛋」,沒錯,這次我們就是要混在一起做成撒尿牛丸package ,
放心,這需求不是今天才出現的,有時網路上找到的lib是cpp或是python ,而又不想各別定義成不同的package,就會需要樣的應用了( 當然你也可以用ctypes來解決 python call c's dll/so ,
只是既然可以用ros2的 topic機制就直接用它比較省事)
參考這兩位大德的解說文: Create a ROS2 package for Both Python and Cpp Nodes 
 
摘要一兩個重點,以供日後回來快速查閱
一.在create package時,請先以「ros2 pkg create  想要的套件名稱  --build-type ament_cmake」指定是要以cpp 為主的package 建置方式,因為日後python要用的setup.py會用不到,重心會放在CMakeLists.txt及pacakge .

假設路徑圖如下:
-------------------------------
my_cpp_py_pkg/
# --> package info, configuration, and compilation
├── CMakeLists.txt
├── package.xml
# --> Python stuff
├── my_cpp_py_pkg
│ ├── __init__.py
│ └── module_to_import.py
├── scripts
│ └── py_node.py
# --> Cpp stuff
├── include
│ └── my_cpp_py_pkg
│ └── cpp_header.hpp
└── src
└── cpp_node.cpp
-------------------------------
CMakeLists.txt方面 
要安裝python node的路徑,就是直接寫做
-----------------
install(PROGRAMS
scripts/py_node.py 
DESTINATION lib/${PROJECT_NAME}
)
-----------------------------
二.「#!/usr/bin/env python3」 -- python node方面,以往是不必在python node的最前面加上做編輯器指定,現在用這種混搭方式就要在py檔的第一行做編輯器指定
三 python node裡要import的話,就上面的路徑而言,是
「from  my_cpp_py_pkg. module_to_import import blablalba」
(不必在CMakeLists.txt裡指定,非常方便)

其他細節就請自己慢慢看上述兩篇引用的網誌了 
(應該沒有IT像我這樣在下班後還在玩ROS的吧....)

2023年1月8日 星期日

ROS2 colcon build遇到的問題 (distro:foxy on ubuntu 20.04)

Q1.錯誤訊息「undefined reference to `rosidl_message_type_support_t const* rosidl_typesupport_cpp::get_message_type_support_handle<sensor_msgs::msg::Imu_<std::allocator<void> > >()' collect2: error: ld returned 1 exit status」

sol:請檢查 CMakeLists.txt 的ament_target_dependencies 及 c_cpp_properties.json includePath 及  package.xml 的 depend  ,套件/路徑有沒有加入(以上述的訊息,是「sensor_msgs」套件定義漏了)

Q2.錯誤訊息「python3.8/site-packages/setuptools/dist.py:717: UserWarning: Usage of dash-separated 'script-dir' will not be supported in future versions. Please use the underscore name 'script_dir' instead 」

sol:除了要「pip install setuptools==58.2.0」把setuptools降級之外,也要把package裡的「setup.cfg」
的「script-dir」及「install-scripts」改為「script_dst」「install_script」

Q3.For python: 執行node時找不到自己定義的模組(明明colcon build都沒錯!!)

sol:請在setup.py的「data_files」裡加上「('lib/' + package_name, [package_name+'/SimpleOneTrailerSystem.py']),」

如:
---------------------
data_files=[
        ('share/ament_index/resource_index/packages',
            ['resource/' + package_name]),
        ('share/' + package_name, ['package.xml']),
        ('lib/' + package_name, [package_name+'/DC_Motor.py']),
    ],
--------
參考這裡

其他的錯誤,日後遇到再回來update了