曆法計算由幾月幾號決定星期幾

開頭                                                                                                   


當電腦或者電子設備只要輸入日期與時間即會自動產生星期幾,基本上這是透過基本公式來產生,因此您並不需要自己設定星期。承襲上一篇衛星時間取得方法,可是衛星時間除了需進行UTC區域時間補償,同時星期也必須自行計算才能得到。

以下使用公式邏輯區分為a、b、m、d 四項                             

日曆上的西元1600年3月1號以後的每一天都表示為西元(100a+b)年m月d日
(1) d就是日曆上的"日"數
(2) m有特殊規定
      3月~12月 m流水號為 1~10;  1月=11; 2月=12
(3) a、b定義
      1 or 2月則為 100a+b=西元年數 -1
      3 ~12月則為 100a+b=西元年數

公式:                                                                                               

d+[2.6m-0.2] - 2a+b+[a/4]+[b/4]
[x]為高斯符號, ex: [3]=3;  [2.4]=2
回傳值: 0-6:星期日, 星期一.... 星期六 以此類推,當值為負數ex:-3, 正確為7-3=4 為星期四

程式撰寫                                                                                        

  • 編譯器:CCS PCWHD
  • 單晶片:Microchip PIC18F45K22

unsigned int8 RTC_Weekend_Transfer(unsigned int8 year_l, unsigned int8 month, unsigned int8 m_day, unsigned int8 year_h=20) 
  unsigned int8 m=0, a=0, b=0;
  a = year_h;
  b = year_l;
  if (month>=1 && month<=2) 
  {
    m = month+10;
    if (b==0) {a--; b=99;}
    else b--;
  }else m = month - 2;
  int w = m_day + (int)(2.6*m-0.2) - 2*a+b + (int)(a/4) + (int)(b/4);
  
  div_t idiv;
  idiv = div(w, 7);    
  w = (idiv.rem+7)%7;  //當餘數為負值時透過此程序可以轉正
  return w;  
}

閏年計算不是單單每四年一閏

閏年已知每四年一閏,每100年不閏,每400年一閏

閏年=2月29天;
平年=2月28天


程式撰寫                                                                                                                                 
boolean RTC_isLeapYear(unsigned int8 year_l, unsigned int8 year_h=20)
{
  long year=(long)year_h*100+year_l;
  if ((year%400) == 0) return true;
  else if ((year%4)==0 && (year%100)!=0) return true;
  return false;
}

GPS NMEA protocol 單晶片資訊解析與定時校正

GPS 通訊模組系統,透過RS232通訊介面可獲得衛星時間與方位
NMEA((National Marine Electronics Association)0183 ASCII 的格式
本人將使用收到的 RMC 資料串進行解析,取出衛星當下的時間



Message Structure:                                                                           

$GPRMC,hhmmss,status,latitude,N,longitude,E,spd,cog,ddmmyy,mv,mvE,mode*cs<CR><LF>

Example:                                                                                            

$GPRMC,083559.00,A,4717.11437,N,00833.91522,E,0.004,77.52,091202,,,A*57

Field No. Example Format Name Unit Description
0           $GPRMC string $GPRMC - Message ID, RMC protocol header
1           083559.00 hhmmss.sss hhmmss. - UTC Time, Time of position fix
2           A character Status - Status, V = Navigation receiver warning, A = Data
valid, see Position Fix Flags description
3           4717.11437 ddmm.mmmm Latitude - Latitude, Degrees + minutes, see Format description
4           N character N - N/S Indicator, hemisphere N=north or S=south
5           00833.91522 dddmm.
Mmmm
Longitude - Longitude, Degrees + minutes, see Format description
6           E character E - E/W indicator, E=east or W=west
7           0.004 numeric Spd knots Speed over ground
8           77.52 numeric Cog degrees Course over ground
9           091202 ddmmyy date - Date in day, month, year format
10          - numeric mv degrees Magnetic variation value, not being output by receiver
11          - character mvE - Magnetic variation E/W indicator, not being output by receiver
12          - character mode - Mode Indicator, see Position Fix Flags description
13          *57 hexadecimal cs - Checksum
14          - character <CR><LF> - Carriage Return and Line Feed
  


程式撰寫                                                                                          

  • 使用編譯器: CCS PCWHD
  • 使用單晶片:Microchip PIC18F45K22


透過剛剛介紹的$GPSRMC 接收來自衛星訊號的ASCII內容,裡面包含了有UTC標準時間,本文透過解析內容並轉為資料串再回傳給單晶片修正RTC時間
副程式除了解析出時間外,同時也將轉換經緯度座標位置
char *srg: 傳入接收來自於RS232資料串
TDateTime *DT: 傳入欲擺放時間日期等資訊的結構串
TCoordinate *Coordinate:傳入欲擺放經緯度等資訊的結構串
程序完成後成功傳為0
int GPS_GPRMC_stream_decoder(char *srg, TDateTime *DT, TCoordinate *Coordinate)
{   
  char st = 0, ed = 0, index = 0;
  //step1: find $GPRMC position,not found reutn value = -1
  if (strFind(srg, (char*)"$GPRMC", 0) == -1)
    return -1;
  
  //step2: find first ',' position in string
  st = charFind(srg, ',', 0);
  if (st == -1) 
    return -1;
    
  char str[16];
  char str_length = strlen(srg);
  while (st != -1 && index < str_length)
  {
    ed = charFind(srg, ',', st+1);
    switch (++index)
    {
      
      case 1 : DT->hour = atoi(StrmnCpy(str, srg, st+1, 2));
               DT->min  = atoi(StrmnCpy(str, srg, st+3, 2));
               DT->sec  = atoi(StrmnCpy(str, srg, st+5, 2));
               break;
      case 2 : if (*StrmnCpy(str, srg, st+1, 1) != 'A')
                 return -1;
               break;
      case 3 : StrmnCpy(str, srg, st+1, ed-st-1);
               Coordinate->Latitude = GPS_StrtoLatitude(&str, ed-st-1); 
               break;
      case 4 : StrmnCpy(str, srg, st+1, 1);
               Coordinate->N_S = str[0];
               break;        
      case 5 : StrmnCpy(str, srg, st+1, ed-st-1);
               Coordinate->Longitude = GPS_StrtoLongitude(&str, ed-st-1);
               break;
      case 6 : StrmnCpy(str, srg, st+1, 1);
               Coordinate->E_W = str[0];
               break; 
      case 7 : StrmnCpy(str, srg, st+1, ed-st-1);
               Coordinate->speed  = strtof(str, '0');
               break; 
      case 9 : DT->m_day   = atoi(StrmnCpy(str, srg, st+1, 2));
               DT->month   = atoi(StrmnCpy(str, srg, st+3, 2));
               DT->year    = atoi(StrmnCpy(str, srg, st+5, 2));
               DT->w_day   = RTC_Weekend_Transfer(20, DT->year, DT->month, DT->m_day); 
               break;
    }
    st = ed;
  }
  return 0;
}     

尋找字串中指定的字串內容處在位置
int strFind(char *srg, char *s, size_t st)
{
  BYTE pos; 
  for (pos=st, srg+=st; *srg != '\0'; srg++, pos++)
    if (strspn(srg, s) == strlen(s))  
      return pos;
  return -1;
}

尋找字元中指定的字串內容處在位置
int charFind(char *s, char c, size_t st)
{
  BYTE pos;
  for (pos=st, s+=st; *s != '\0'; s++, pos++)
    if (*s == c)  
      return pos;
  return -1; 
}

複製字串
char *StrmnCpy(char *str, char *srg, size_t m, size_t n)
{
  int i=0;
  char *s;
 
  for(s=str, i=m, srg+=m; i<m+n; i++)
    *s++ = *srg++;
  *s++ = '\0';
   
  return str;
}

NEC IR protocol 紅外線接收器程式撰寫

紅外線接收器有區分很多款規格,這篇文章主要針對NEC 38KHz規格進行說明,文章以下使用圖示,部分參考原文出處,使用於非商業性質,僅供教育說明

NEC Protocol                                                                                  

如下圖所示,NEC format 由 以下組成

  • Leader code, 8 bits address and 8 bits command length
  • Address and Command 傳送兩次,第二次為反向結果可以作為錯誤檢知使用,或者擴展成16bits
  • 38KHz Carrier frequncy
  • Pulse distance modulation
  • Bit time 為 1.125ms 或者  2.25ms 

Modulation                                                                                 

NEC protocol使用 pulse distance encoding調變技術

  • 560us 載波與space組成 2.25ms 波型代表 位元 "1"
  • 560us 載波與space組成 1.12ms 波型代表 位元 "0"






訊號接收,目前紅外線接收可透過模組IC進行處理,只要挑選對應正確的載波訊號,紅外線接收器即可將訊號轉成方波輸出,以下圖示為 PANASONIC PNA4612M (38KHz) 原理圖
操作上非常方便,指要依序接上VCC(+5V) GND供應電源後,Vout即為輸出腳可接拉到MCU接收腳位,中間不需再連接或者上拉電阻元件,大大減輕單晶片處理載波訊號難度


現在紅外線為很普及的產品,使用經驗上通常按下按鈕後,發出紅外線讓接收器接收,進行達到 "Remote control"的目的,可是操作應該有發現,例如持續按下聲音大小聲按鈕時,不必放開按鍵,就可以持續觸發,通常作法並非一直發送相同Address+command訊號,而是連續輸出Repeat code 間隔110ms 訊號輸出方式

Repeat code 由9ms載波和 2.25ms space 組成,每間隔110ms傳送一次

參考資料連結: http://www.sbprojects.com/knowledge/ir/nec.php


程式撰寫                                                                                        


  • C編譯器 :CCS PCWHD 
  • MCU: Microchip PIC18F45K22


紅外線接收器,接收來自於紅外線發射的載波後進行解析,並且輸出方波,前面文章已經說明透過pulse ON/OFF 不同時間作為表示bit 1 or 0,收接資料後單晶片並無法短時間馬上解析出結果,最保險方式就是搭配timer 計時器技術當下每個脈波花的總時間,完成接收全部資料後,再透過旗標flag進行解析脈波,再轉換成正確的資料 Address and Command

Microhcip PIC18F45K22 共計有  多組Timer可供使用,從以上資訊得知最長時間會達到110ms,本文採用Timer1 16bits 並搭配Prescaler 1/2/4/8 擴展timer1 overflow的時間




TIMER1 configuration,依序算出每個波型對應時間長度
Interrupt rate:  T1_INTERNAL|T1_DIV_BY_4 
4/64000000*65536*4 = 16.384 ms  (resolution:0.0625*4=0.25us)
Leader: 13.5ms(54000) (13.5*1000)/0.25us
"1": 2.25 ms (9000)  (2.25*1000)/0.25us
"0": 1.12 ms (4480)  (1.12*1000)/0.25us

#define IR_LEADER_MIN       50000  
#define IR_LEADER_MAX       58000
#define IR_ZERO_MIN         4000
#define IR_ZERO_MAX         5000
#define IR_ONE_MIN          8000
#define IR_ONE_MAX          9900

定義資料群組,資料量總共 32 bits 搭配一組Leader 與 第一組 garbage訊號(需忽略),最大資料達16bits
建置34組 long資料Array
/* irframes[0] (start) will be garbage, ignore it... *_-
** irframes[1] NEC Leader code
** irframes[2~9]   custom code
** irframes[10~17] custom code'
** irframes[18~25] data code
** irframes[26~33] data code'
*/
#define irFramesCounts 34
long    irFrames[irFramesCounts];



搭配單晶片CCP 模組下緣觸發(H->L)訊號,或者採用RB 中斷觸發訊號,藉由旗標判斷資料接收過程,一旦完成接收,flag轉為true,緊接就可以進行每個資料量判斷,進而解析紅外接收資料後處理模式,並且判斷正反訊號正確性,程序完成後組成兩個 8bit int 資料返回
if ((irFrames[1] < IR_LEADER_MIN) && (irFrames[1] > IR_LEADER_MAX))
    return -1;
  
  for (i=0; i<8; i++)
  {
    // custom code
    custom_ptr = i+2;
    if ((irFrames[custom_ptr] >= IR_ZERO_MIN) && (irFrames[custom_ptr] <= IR_ZERO_MAX)) shift_right(&custom, 1, 0);
    else  if ((irFrames[custom_ptr] >= IR_ONE_MIN) && (irFrames[custom_ptr] <= IR_ONE_MAX)) shift_right(&custom, 1, 1);
      
    //custom' code
    _custom_ptr = i+10;
    if ((irFrames[_custom_ptr] >= IR_ZERO_MIN) && (irFrames[_custom_ptr] <= IR_ZERO_MAX)) shift_right(&_custom, 1, 0);
    else  if ((irFrames[_custom_ptr] >= IR_ONE_MIN) && (irFrames[_custom_ptr] <= IR_ONE_MAX)) shift_right(&_custom, 1, 1);
      
    // data code
      data_ptr = i+18;
      if ((irFrames[data_ptr] >= IR_ZERO_MIN) && (irFrames[data_ptr] <= IR_ZERO_MAX)) shift_right(&data, 1, 0);
      else  if ((irFrames[data_ptr] >= IR_ONE_MIN) && (irFrames[data_ptr] <= IR_ONE_MAX)) shift_right(&data, 1, 1);
      
    // data' code
    _data_ptr = i+26;
    if ((irFrames[_data_ptr] >= IR_ZERO_MIN) && (irFrames[_data_ptr] <= IR_ZERO_MAX)) shift_right(&_data, 1, 0);
    else  if ((irFrames[_data_ptr] >= IR_ONE_MIN) && (irFrames[_data_ptr] <= IR_ONE_MAX)) shift_right(&_data, 1, 1);
  }

  if ((custom != (_custom^0xFF)) || (data != (_data^0xFF)))
    return -1;


實驗結果                                                                             

這是一款很常見的紅外線遙控器模組,先行再每個按鈕位置上標註上Command資料,實驗上搭配Microchip PicKit3實驗板,進行驗證測試結果。 LCD顯示Leader 顯示13.44ms(正常都有誤差値,撰寫軟體需留意),Custom(Address) :00 這款沒有定義用戶碼,接收到data編號為:45為筆者按下Power 按鈕時解析出來的結果,驗證結果成功


 







高效率 DC-DC Nixie Tube 升壓版 Part3

這次發表為新一代專為Nixie Tube設計的DC-DC 高壓版
此款同樣採用DC-DC 技術,只要單一電源輸入12V 最高可輸出15W 效率達90%

請出了小朋友的鱷魚小寶貝當model,這款電路板尺寸只有48mm*44mm大小
採用變壓器統合方案,提高轉換效率,藉此可增加輸出功率



使用上承襲part1 電路板插頭樣式,可以抽換式,輸入端採用常見的DC 插頭,方便大部分12V變壓器直接插上使用,電路上可搭配外部控制ON/OFF控制訊號,可以透過外部MCU進行高壓ON/OFF


透過Bourns 3224W 多圈可調整HV 輸出電壓值,透過電阻搭配可以輕易設定輸出電壓範圍
搭配周邊電容與DIODE配置,此款最高設定為最高輸出為250V,功率達15W,已可輕易點亮一般鹵素燈泡


放上一張點亮14 根 IN-18 成果圖




後記: 這是一款高效率DC-DC Step-UP 高壓產生電路,操作時務必做好個人安全保護,通電中不要碰觸任何物件,避免造成觸電