2024年3月10日 星期日

老工程師的技術生活(二十三)--- UROMTER產品維護與支持 (下)

這是繼 老工程師的技術生活(二十二)--- UROMTER產品維護與支持 (上) 的下集文章。

 剛好也可以藉由這兩篇文章,也可以說明一下關於產品開發的幾項重點:包括如何

簡單的平台驗證與測試方法。網路上有很多技術或產品開發的影片,其實都隱含著一種

簡化與快速瀏覽的特性,看起來一般產品好像都很簡單,三兩句就可以交代完了,

只要Follow 如此這般就好像可以變現賺錢似的。等你真正自己按圖索驥做一遍時,

卻又好像不如這些網路影片這麼簡單,這也是許多技術社團臉書、論壇等,常見到別人

提出許多的問題的地方,的那問題出在哪呢?等你看過這篇就知道了。

只要你有看過上集,或有曾經看過我所寫的 USB ROM 模擬器的故事時,就知道這個

產品也是我從園區離職休息半年所開發的東西,也為我賺了第一桶小小的創業基金。

---

在上集我已經完成產品新的USB 驅動程式的安裝,接下來就是把原來在 VC 6.0 開發的

APP 升級成可以支援新的USB 驅動程式的ROM 程式碼下載工具軟體。


VC 6.0 真的是很老的一個PC 端C 語言的開發平台。但他真的是當時最普遍的C 語言開發平台。

當初我也是為了這個產品,才開始接觸學習這個程式開發平台。現在再回頭看自己寫的

程式,真的是又菜又呆啊~不過,他搭配硬體銷售,也沒聽過市場客戶反映甚麼軟體使用上

的問題。真的是奇蹟啊。不過,這也是給初學者的我一股學習信心啊。

當然當初VC 6.0 的平台應用在今日PC 平台與作業系統下,會有很多問題的。所以當你用

新一代的 Visual Studio 平台打開組譯時,會有上圖中許多錯誤與警告。這些都是小事,

只要有點耐心就可以完全處理掉的。(只要你有持續常寫這類APP 應用軟體的話。😂)

然後利用WINUSB 的範例程式,就可以把軟體中USB 驅動程式介面給抽換成WINUSB 了。

之後就可以簡單地透過USB 分析儀看一下把ROM 資料下載到ROM 模擬器上的結果了:


上圖左邊是原來欲下載到 ROM 的 binary 檔,這是不管哪一種MCU 或系統平台。 

我們都是把這些Binary 內容給燒錄到外部的 ROM 內(譬如 : 27C512 或是 39F040 等這一類)

當然啊,現在很多平台都已經改成 Serial Flash ROM( 25Cxx) 這一類了,甚至是直接內含

在 MCU 平台中了。不過這一種外掛的 EEPROM 的確有一陣子流行與使用方式的。

所以使用這一種 ROM 模擬器就提供一個方便快的開發方式,往下看範例就知道。

---

當我們在 PC 端上已經完成這樣的讀取 Binary 檔,並成功的下載到模擬器之後,我們就得

要找個驗證平台來試試可不可以讓Target 系統給跑起來?當然這也是我產品量產QC 品管

流程之一,以確保產品出貨後,是可以正確使用操作的


上圖的這塊電路板平台是以前我老東家同事提供給我作為 ROM 模擬器的QC 平台:

是一塊車載多媒體撥放平台,但時代久遠,也真的找不到與確認原來的Binary 檔是否

正確?而且我對這塊系統PCB 也不懂,根本無法偵錯與硬體除錯....只好另尋方法。

而我原來也有一台萬用型的燒錄器,也因年久不用也出現問題。所以翻出一度本來

想丟回收箱的一塊PCB 開發電路板:他剛好就是使用外掛 EEPROM 來跑系統的。


不過他是使用 RDC 的 8052 。不好意思,我ROM 模擬器裡用的 USB Controller SOC 中的

MCU 就是當年用 RDC 的 8052 IP ,也剛好被我抓到他 IP 中有個 Timer2 嚴重Bug 。原先

我們公司IC 設計者也不相信,然後呢?我就寫了一個只有兩三行的組合語言,就可以重現

這個 Timer2 的 Bug,IC 設計者可以很輕易的在他們的IC 設計的模擬平台中發現這個 bug 。

然後就把 RDC 公司的IP 設計人員給叫過來,我只記得他們回去沒多久,就完全停售這款

號稱兼容 Intel 或市面上 8052 的MCU 了。 是不是跟這個問題有關?我就不得而知了。

(當然。只要你能發現IP 設計上的 bug ,其實你就可以找到一些韌體上可以迴避與跳過的

方法,只是就有點不是很相容就是了。我們家 USB Controller SOC 也賣了幾千萬顆啊。)



反正啊,我只是要個簡單的驗證平台而已,又不是要幫原廠Debug MCU ,所以我就直接

把原來的MCU 換成 Atmel 的 8052 照樣可以跑啊。接下來就如何取代原來的EEPROM 了。

我想很多剛入門的新手或系統開發者一定就是直接把原來的範例程式直接燒錄成 Binary 

檔案給直接下載就好了:就網路上一般人喜歡幹的事一樣,然後就卡住了。(附註一)

當初取得這塊開發版時,原始資料與程式碼完全不能用,也沒有燒錄檔資料。

幸好我們還有一點系統開發基礎,所以就自行處理了。也根本不要用甚麼複雜的 LCM 模組。

你怎麼快速簡單的確認平台呢?你不要以為接個 LCM 就很簡單,其實還是很複雜的啦。

那我們就用最簡單的 Keil C51 開發平台來寫個簡單的程式吧:

---

sbit bRxd			= P3^0;
sbit bTxd			= P3^1;

void main(void)
{

		P3=0xFF;
		//--Break Loop
		while(1){
			bTxd=~bTxd;
			_nop_();
		}
}

這種程式碼根本不會有任何函數庫引用,直接看所產生的組合語言與產生Binary 檔內容:

 


我們可以看到 Startup.A51 設定完 Stack Point 寄存器之後就直接跳到 main()。Binary 檔

的內容就是:跳到SP 初始化(0x0B 位置), 然後又跳回 0x03 位置執行 main();

然後就可以把這 17 Bytes 的 Binary 下載到 ROM 模擬器了:


我們就可以看到電路板上的 UART 的Tx (P3.1) 就產生一個簡單的 I/O Toggle 信號了。

(附註二)

到目前為止,我們就可以確認我們的 ROM 模擬器可以完全取代電路板上的 EPROM 了。

( 27C512) 。硬體線路沒問題的。

你不要以為寫這種只有 17 Bytes 的程式碼很沒營養,它卻很基礎很好用的。但許多

網路上寫程式的新手或學生,就喜歡一下跳到很難很複雜的系統或程式碼中,

以為一下子就可以成仙了。這種非常小、簡單沒營養的程式花你不多久的時間,

但可以大大縮短減少你許多系統開發的冤枉路的。也可以培養你系統除錯能力的

---

但是又有一個系統驗證的盲點與問題點了:我怎麼把程式撐到 64K Bytes,以確保我的

系統可以全程讀取 ROM 呢?這是一個簡單的系統開發與驗證的經驗

我們可以在程式裡動手腳,我們可以在Startup.A51 的組合語言裡建立一個簡單的 Table:


$NOMOD51
;------------------------------------------------------------------------------
;  This file is part of the C51 Compiler package
;  Copyright (c) 1988-2005 Keil Elektronik GmbH and Keil Software, Inc.
;  Version 8.01
;
;  *** <<< Use Configuration Wizard in Context Menu >>> ***
;------------------------------------------------------------------------------
;  STARTUP.A51:  This code is executed after processor reset.
;
;  To translate this file use A51 with the following invocation:
;
;     A51 STARTUP.A51
;
;  To link the modified STARTUP.OBJ file to your application use the following
;  Lx51 invocation:
;
;     Lx51 your object file list, STARTUP.OBJ  controls
;
;------------------------------------------------------------------------------
;
;  User-defined  Power-On Initialization of Memory
;
;  With the following EQU statements the initialization of memory
;  at processor reset can be defined:
;
;  IDATALEN: IDATA memory size <0x0-0x100>
;      Note: The absolute start-address of IDATA memory is always 0
;            The IDATA space overlaps physically the DATA and BIT areas.
IDATALEN        EQU     80H
;;IDATALEN        EQU     0H
;
;  XDATASTART: XDATA memory start address <0x0-0xffff> 
;      The absolute start address of XDATA memory
XDATASTART      EQU     0     
;
;  XDATALEN: XDATA memory size <0x0-0xffff> 
;      The length of XDATA memory in bytes.
XDATALEN        EQU     0      
;
;  PDATASTART: PDATA memory start address <0x0-0xffff> 
;      The absolute start address of PDATA memory
PDATASTART      EQU     0H
;
;  PDATALEN: PDATA memory size <0x0-0xff> 
;      The length of PDATA memory in bytes.
PDATALEN        EQU     0H
;
;
;------------------------------------------------------------------------------
;
; Reentrant Stack Initialization
;
;  The following EQU statements define the stack pointer for reentrant
;  functions and initialized it:
;
;  Stack Space for reentrant functions in the SMALL model.
;   IBPSTACK: Enable SMALL model reentrant stack
;      Stack space for reentrant functions in the SMALL model.
IBPSTACK        EQU     0       ; set to 1 if small reentrant is used.
;   IBPSTACKTOP: End address of SMALL model stack <0x0-0xff>
;      Set the top of the stack to the highest location.
IBPSTACKTOP     EQU     0xFF +1     ; default 0FFH+1  
; 
;
;  Stack Space for reentrant functions in the LARGE model.      
;   XBPSTACK: Enable LARGE model reentrant stack
;      Stack space for reentrant functions in the LARGE model.
XBPSTACK        EQU     0       ; set to 1 if large reentrant is used.
;   XBPSTACKTOP: End address of LARGE model stack <0x0-0xffff>
;      Set the top of the stack to the highest location.
XBPSTACKTOP     EQU     0xFFFF +1   ; default 0FFFFH+1 
; 
;
;  Stack Space for reentrant functions in the COMPACT model.    
;   PBPSTACK: Enable COMPACT model reentrant stack
;      Stack space for reentrant functions in the COMPACT model.
PBPSTACK        EQU     0       ; set to 1 if compact reentrant is used.
;
;    PBPSTACKTOP: End address of COMPACT model stack <0x0-0xffff>
;      Set the top of the stack to the highest location.
PBPSTACKTOP     EQU     0xFF +1     ; default 0FFH+1  
; 
;
;------------------------------------------------------------------------------
;
;  Memory Page for Using the Compact Model with 64 KByte xdata RAM
;  Compact Model Page Definition
;
;  Define the XDATA page used for PDATA variables. 
;  PPAGE must conform with the PPAGE set in the linker invocation.
;
; Enable pdata memory page initalization
PPAGEENABLE     EQU     0       ; set to 1 if pdata object are used.
;
;  PPAGE number <0x0-0xff> 
;  uppermost 256-byte address of the page used for PDATA variables.
PPAGE           EQU     0
;
;  SFR address which supplies uppermost address byte <0x0-0xff> 
;  most 8051 variants use P2 as uppermost address byte
PPAGE_SFR       DATA    0A0H
;
; 
;------------------------------------------------------------------------------

; Standard SFR Symbols 
ACC     DATA    0E0H
B       DATA    0F0H
SP      DATA    81H
DPL     DATA    82H
DPH     DATA    83H

                NAME    ?C_STARTUP


?C_C51STARTUP   SEGMENT   CODE
?STACK          SEGMENT   IDATA

                RSEG    ?STACK
                DS      1

                EXTRN CODE (?C_START)
                PUBLIC  ?C_STARTUP

                CSEG    AT      0
?C_STARTUP:     LJMP    STARTUP1

                RSEG    ?C_C51STARTUP

STARTUP1:

IF IDATALEN <> 0
                MOV     R0,#IDATALEN - 1
                CLR     A
IDATALOOP:      MOV     @R0,A
                DJNZ    R0,IDATALOOP
ENDIF

IF XDATALEN <> 0
                MOV     DPTR,#XDATASTART
                MOV     R7,#LOW (XDATALEN)
  IF (LOW (XDATALEN)) <> 0
                MOV     R6,#(HIGH (XDATALEN)) +1
  ELSE
                MOV     R6,#HIGH (XDATALEN)
  ENDIF
                CLR     A
XDATALOOP:      MOVX    @DPTR,A
                INC     DPTR
                DJNZ    R7,XDATALOOP
                DJNZ    R6,XDATALOOP
ENDIF

IF PPAGEENABLE <> 0
                MOV     PPAGE_SFR,#PPAGE
ENDIF

IF PDATALEN <> 0
                MOV     R0,#LOW (PDATASTART)
                MOV     R7,#LOW (PDATALEN)
                CLR     A
PDATALOOP:      MOVX    @R0,A
                INC     R0
                DJNZ    R7,PDATALOOP
ENDIF

IF IBPSTACK <> 0
EXTRN DATA (?C_IBP)

                MOV     ?C_IBP,#LOW IBPSTACKTOP
ENDIF

IF XBPSTACK <> 0
EXTRN DATA (?C_XBP)

                MOV     ?C_XBP,#HIGH XBPSTACKTOP
                MOV     ?C_XBP+1,#LOW XBPSTACKTOP
ENDIF

IF PBPSTACK <> 0
EXTRN DATA (?C_PBP)
                MOV     ?C_PBP,#LOW PBPSTACKTOP
ENDIF

                MOV     SP,#?STACK-1

; This code is required if you use L51_BANK.A51 with Banking Mode 4
; Code Banking
;  Select Bank 0 for L51_BANK.A51 Mode 4
#if 0   
;      Initialize bank mechanism to code bank 0 when using L51_BANK.A51 with Banking Mode 4.
EXTRN CODE (?B_SWITCH0)
                CALL    ?B_SWITCH0      ; init bank mechanism to code bank 0
#endif
;
                LJMP    ?C_START
;;---
				CSEG    AT 01000H
CSignature10:
				DB	001H
					
				CSEG    AT 02000H
CSignature20:
				DB	002H

				CSEG    AT 03000H
CSignature30:
				DB	003H
					
				CSEG    AT 04000H
CSignature40:
				DB	004H
					
				CSEG    AT 05000H
CSignature50:
				DB	005H
					
				CSEG    AT 06000H
CSignature60:
				DB	006H
					
				CSEG    AT 07000H
CSignature70:
				DB	007H

				CSEG    AT 08000H
CSignature80:
				DB	080H

				CSEG    AT 09000H
CSignature90:
				DB	090H

				CSEG    AT 0A000H
CSignatureA0:
				DB	0A0H

				CSEG    AT 0B000H
CSignatureB0:
				DB	0B0H
					
				CSEG    AT 0C000H
CSignatureC0:
				DB	0C0H

				CSEG    AT 0D000H
CSignatureD0:
				DB	0D0H

				CSEG    AT 0E000H
CSignatureE0:
				DB	0E0H

				CSEG    AT 0F000H
CSignatureF0:
				DB	0F0H

				CSEG    AT 0FFFEH
CSignatureFE:
				DB	055H
				DB	0AAH

                END

 ---

我們可以分別在 64KBytes 中,每個一段位置裡擺上一個檢查碼。然後最後位置擺上

個 0x55AA 就可以了。

就簡單的寫個printf 從 UART 丟字串出來就可以了:以下就是整個 main.c 就可以了:


/* main.c for NEW 8051 KIT */

#include 
#include 
#include 
#include 
#include 
//#include 
//#include "lcd.h"
//---
//-----------------------------------------------------------------------------

//sfr P3 = 0xB0;          /* SFR definition for Port 3 */
//---
sfr P3_0_bit = 0xB0;
sfr P3_1_bit = 0xB1;
sfr P3_2_bit = 0xB2;
sfr P3_3_bit = 0xB3;
sfr P3_4_bit = 0xB4;
sfr P3_5_bit = 0xB5;
sfr P3_6_bit = 0xB6;
sfr P3_7_bit = 0xB7;
//---
sbit DBp00 		= P0^0;						// Debug I/O
sbit DBp01 		= P0^1;						// Debug I/O
sbit DBp02		= P0^2;						// Debug I/O
//--
sbit bLCD_RS	= P1^5;						// LCD RS Pin
sbit bLCD_RW	= P1^6;						// LCD RW Pin
sbit bLCD_EN	= P1^7;						// LCD RS Pin

sbit RS485En 	= P2^0;
sbit Led1  		= P2^1;                      // LED='1' means ON
//sbit Led1  		= P1^5;                      // LED='1' means ON
sbit Led2  		= P2^2;
sbit Led3  		= P2^3;                      // LED='1' means ON

sbit bRxd			= P3^0;
sbit bTxd			= P3^1;

sbit bEN_A 		= P3^2;
sbit bEN_B		= P3^3;


#define INTERVAL       	9217/5  /* 2mS FOR 11.0592MHz CLOCK */
#define BAUD_RATE 			0xfd    /*  9600 BPS */
#define TMH             (65536-INTERVAL) / 256
#define TML             (65536-INTERVAL) % 256


#define  SRAM1        XBYTE[0x0000]           /* 0000H-7FFFH */
#define  SRAM2        XBYTE[0x8000]           /* 8000H-FF5FH */

#define  IC7P1_PA			XBYTE[0xff60]      	/* output port A0:0 A1:0*/
#define  IC7P1_PB    	XBYTE[0xff61]				// A0=1, A1=0
#define  IC7P1_PC    	XBYTE[0xff62]				// A0=0, A1=1
#define  IC7P1_CNTL  	XBYTE[0xff63]				// A0=1, A1=1

#define  IC8P2_PA    	XBYTE[0xff70] 	   /* output port */
#define  IC8P2_PB    	XBYTE[0xff71]      /* PA,PB,PC output */
#define  IC8P2_PC    	XBYTE[0xff72]
#define  IC8P2_CNTL		XBYTE[0xff73]

#define  LCD_DATA			IC8P2_PB          		/* 8255 Latch */
#define  LCD_CNTL			IC8P2_CNTL        		/* 8255 Latch */
#define  DISP_L1    	1
#define  DISP_L2     	2

#define  RTC          XBYTE[0xff80]  	//0xff80
#define  SECOND				XBYTE[0xff80]		//RTC+0x00
#define  MINUTE				XBYTE[0xff82]		//RTC+0x02
#define  HOUR					XBYTE[0xff84]		//RTC+0x04
#define  WEEK 				XBYTE[0xff86]		//RTC+0x06
#define  DATE         XBYTE[0xff87]  	//RTC+0x07
#define  MONTH        XBYTE[0xff88]		//RTC+0x08
#define  YEAR         XBYTE[0xff89]		//RTC+0x09

#define  RTCREG_A     XBYTE[0xff8a]  	//RTC+0x0a
#define  RTCREG_B     XBYTE[0xff8b]		//RTC+0x0b
#define  RTCREG_C			XBYTE[0xff8c]		//RTC+0x0c
#define  RTCREG_D			XBYTE[0xff8d]		//RTC+0x0d

///====
unsigned char code Siguature10 _at_ 0x1000;  
unsigned char code Siguature20 _at_ 0x2000;  
unsigned char code Siguature30 _at_ 0x3000;  
unsigned char code Siguature40 _at_ 0x4000;  
unsigned char code Siguature50 _at_ 0x5000;  
unsigned char code Siguature60 _at_ 0x6000;  
unsigned char code Siguature70 _at_ 0x7000;  
unsigned char code Siguature80 _at_ 0x8000;  
unsigned char code Siguature90 _at_ 0x9000;  
unsigned char code SiguatureA0 _at_ 0xA000;  
unsigned char code SiguatureB0 _at_ 0xB000;  
unsigned char code SiguatureC0 _at_ 0xC000;  
unsigned char code SiguatureD0 _at_ 0xD000;  
unsigned char code SiguatureE0 _at_ 0xE000;  
unsigned char code SiguatureF0 _at_ 0xF000;  
unsigned int code SiguatureFE _at_ 0xFFFE;  
//uint8 code SiguatureFF _at_ 0xFFFF;  

void init_8255();
void UartInit(void);
void delay_ms(unsigned int t);

void main(void)
{
 unsigned int m;
 //unsigned char n,k;
 unsigned char n;

    //output(P3,0xff);
    P3=0xFF;

    init_8255();
    UartInit();	
    printf("\n\rUROMTER II EV Board Initialiaze\n\r");
    //
    n = Siguature10;
    printf("Signature 10:0x%02bX\n\r", n);
    delay_ms(300);
    n = Siguature20;
    printf("Signature 20:0x%02bX\n\r", n);
    delay_ms(300);
    n = Siguature30;
    printf("Signature 30:0x%02bX\n\r", n);
    delay_ms(300);
    n = Siguature40;
    printf("Signature 40:0x%02bX\n\r", n);
    delay_ms(300);
    n = Siguature50;
    printf("Signature 50:0x%02bX\n\r", n);
    delay_ms(300);
    n = Siguature60;
    printf("Signature 60:0x%02bX\n\r", n);
    delay_ms(300);
    n = Siguature70;
    printf("Signature 70:0x%02bX\n\r", n);
    delay_ms(300);
    n = Siguature80;
    printf("Signature 80:0x%02bX\n\r", n);
    delay_ms(300);
    n = Siguature90;
    printf("Signature 90:0x%02bX\n\r", n);
    delay_ms(300);
    n = SiguatureA0;
    printf("Signature A0:0x%02bX\n\r", n);
    delay_ms(300);
    n = SiguatureB0;
    printf("Signature B0:0x%02bX\n\r", n);
    delay_ms(300);
    n = SiguatureC0;
    printf("Signature C0:0x%02bX\n\r", n);
    delay_ms(300);
    n = SiguatureD0;
    printf("Signature D0:0x%02bX\n\r", n);
    delay_ms(300);
    n = SiguatureE0;
    printf("Signature E0:0x%02bX\n\r", n);
    delay_ms(300);
    n = SiguatureF0;
    printf("Signature F0:0x%02bX\n\r", n);
    delay_ms(300);
    m = SiguatureFE;
    printf("Signature End:0x%04X\n\r", m);
    delay_ms(300);
	
    //--Break Loop
    while(1){
    	//bTxd=~bTxd;
	//_nop_();
    	IC8P2_PA=0x55;			
	IC8P2_PB=0x01;			
    	IC8P2_PC=0x00;			
    	delay_ms(300);
	IC8P2_PA=0xAA;			
    	IC8P2_PB=0x00;			
	IC8P2_PC=0x01;			
	delay_ms(300);
    }
}
//----
void init_8255()
{
        IC7P1_CNTL = 0x9b;     /* PA,PB,PC all input */
        IC7P1_PA = 0xff;
        IC7P1_PB = 0xff;
        IC7P1_PC = 0xff;

        IC8P2_CNTL = 0x80;      /* PA & PB & PC output */
        IC8P2_PA = 0x00;
        IC8P2_PB = 0x00;
        IC8P2_PC = 0xff;
}
//----
void UartInit(void)        //9600bps@11.0592MHz
{
		TMOD = 0x20; //use timer 1, 8bit, auto reload
		SCON = 0x50; // serial mode 1, 8bit with 1 start n stop bit,,, enable reception
		TH1 = 0xFD; // 9600 baud rate
		TR1 = 1;
		TI=1;
		RI=0;	
}
//
void delay_ms(unsigned int t)
{
    unsigned char a,b;
    while(t--)
    {
      for(b=102;b>0;b--)
      for(a=3;a>0;a--);
    }
}

這樣子就算是一個簡單又完整的USB ROM 模擬器的 QC 驗證韌體了。

我們就可以簡單輕鬆的開發 8051 的系統了,也不需要燒錄器,也不需要額外的開發下載

平台,有 64KBytes 可以讓你慢慢地寫 8051 的韌體了。至於 I/O 夠不夠?或是需要額外的

擴充版?那就看你的系統開發需求為何吧?說真的:我還是需要一組簡單的LED 顯示

比較直觀簡單。UART 輸出還是比較麻煩一點,結果我們就直接看以下影片吧。


從影片中,我們可以很簡單的重新修改韌體,組譯,(改變LED 閃燈模式) 。然就透過

USB ROM 模擬器的下載更新系統平台的韌體,不需要任何燒錄動作

( ROM 模擬器是下載到一組 SRAM 記憶體中的,完全沒有反覆清除燒錄的使用次數限制)。

是不是很方便啊?而且這樣子的系統架構平台在不同的MCU 平台都可以用的。

那怕是 32 Bit 的ARM ,只要是外掛外部記憶體的系統都可以使用。

(喔~我記得以前有位客戶是拿來放機器開機畫面的資料檔的。所以不是拿來寫韌體程式的,

而是一些圖像或影片資料的,這個模擬器是給美工開發人員用來檢視畫面影片用的。)

所以到這樣子,我準備更新 USB ROM 模擬器的系統就可以升級支援Win 7/Win10 了。

----

結語:

這個東西以現在科技的趨勢與眼光來說,也算是落伍退流行的的玩意兒了,但這樣的

技術與開發流程與平台也曾經伴隨許多公司、工程師與產品一段很長的時間,當然也

幫我帶來一筆收入,重點還是我所強調的:這樣的產品技術不重要,重要的是你如何

從技術養成過程中,培養出系統開發、除錯與驗證測試的方法與流程。

學程式撰寫大家都會,但那只是工程上小小的一部分而已。你要如何利用其他的工具

或是在系統上如何設定測試驗證的方法?可以讓你的產品在開發驗證與出貨檢測上

可以輕鬆完成。

工程師磨練技術最終無非就是幾種結果:

1. 一直靠本職學能在某些領域上,可以靠不斷提升自己技能,靠一身本領變現賺錢,

重點是經驗累積的讓你可以長長久久的不斷的套用與延伸擴展。然後有人願意花錢

請你付出你的技術,讓你有錢支付生活費,養家活口的。

2. 利用技術轉換成(包裝)產品出貨,然後爭取訂單換現金收入。

非常重要的一句話:工程師不但要利用技術解決問題,還要能把問題轉換成商機。

就像我這個 USB ROM 模擬器,可以利用USB 介面,提供一個簡單上手協助其他

工程師或許多公司在系統研發上的工具,那就是一個明顯的例子。

----

當然啊,就是因為還有客人還在問這個產品,又因為我當初也沒有碰到未來發生的

Win7/Win10 的USB 驅動問題,現在既然有需求,也有問題,我們就得去解決這些問題。

沒解決,銷售通路與業務就不能接單銷售,你解決了,還能賣多少?賣多久?

那就不是工程師你的問題了。不是嗎?

提供一個簡單的產品開發技術升級的例子給大家參考吧。

謝謝。

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

附註:

這個東西當然不是寫到這裡就結束了,你還是得把這些東西,好好的打包整理,

完整的提供業務通路下載。供客人使用安裝的技術支援工作。

不是技術作完就沒有工程師的事了。沒有額外的產品工程師協助你,還是得你來做啊。

 ---

附註一:

之所以會有這塊開發版是,因為當初我個人開發生產,是透過別人公司平台銷售的。

以站在互惠原則,我也就買了一塊,但就一直擱著沒有拿出來研究或使用過。

所以當初安裝光碟裡,我還幫忙擺了該公司許多其他產品介紹。但畢竟也不是

專業的業務銷售通路,我還是另外找到專業的儀器銷售通路,才把生意做起來的。

其實這塊學習版所給的範例程式也沒有說明開發平台是甚麼?而且也很多地方交代不清。

原始程式碼也不完整,甚至連簡單的 Binary (HEX) 也沒給?這以現在開發版的銷售市場

文化來說:根本完全不及格的,這怎麼會有銷售量呢?哪怕你開發版設計得多好?

多便宜?(更何況也不便宜啊!) 也沒啥用啊。完全不符合新手入門或學習者的需求。

(就算這塊 PCB 多好賺? 也沒多少經濟效應啊。有價沒量的產品市場永遠是老闆業務

會不喜歡 RD 工程師腦袋想法的地方。搞技術與做生意是兩碼子的事。還要備一大堆料

而且還是有 bug 的MCU 啊。😢😢😢😢😢)

附註二:

這又是這塊學習版另一重大缺失:沒有簡單的I/O 輸出或LED 燈號,不是每個新手或

使用者可以快速的從硬體電路板上去找到簡單快速的結果輸出的啦。

當然當時的學習單晶片的時空背景不同,後來因為 Arduino 開放平台出現,許多單晶片

的學習開發就完全走向開放與輕鬆上手的環境。那就更不用說許多周邊商品了。

我還記得當初安裝光碟裡,還有該公司對於未來產品的規劃里程碑。這也剛好可以

事後諸葛的拿來檢視這樣的產品規劃對不對?

其實最重要的還是在於從這些反省檢視中,我們從中可以學習到甚麼值得我們

年輕人可以借鏡的?有很多對於未來的規劃都不是我們一、兩個人可以看得準的,

更何況你還用太專業領域,過於狹隘的角度來看未來,其實很容易讓自己很難

大鳴大放的從宏觀的角度來看產品?或市場趨勢?這也是許多小公司或小團體

容易迷失的地方,或許自己也清楚,但資源(資金)不足,能改變的條件也不多,

能真正付之行動也有限。結果就是如此。就連維持一個簡單的產品銷售獲利的

能力都有問題。

或許從這兩篇的產品技術文章,也可以讓大家多一點思考這些技術以外的東西。

難的不是這些技術或產品,或是幾年後的市場趨勢或機會?而是你自己從現在

開始你做了甚麼準備?讓你未來可以立於不敗呢?這才是重點。畢竟工程師的

技術人生就是一條不歸路的單行道啊。

加油吧。祝福大家。

10 則留言:

  1. "畢竟工程師的技術人生就是一條不歸路的單行道啊。"
    不只,我的朋友兒子前年美工科畢業,當年就遇到AI生圖,等於一畢業又要開始重頭學AI生圖,2D美工市場差不多廢了。我想3D美工說不定可以多幾年時間,看來今年又差不多了,今年開始又有文字生成小影片,小電影看來也不行了。
    美工有全廢嗎?當然不是,企劃還是要人腦,審美還是要人腦,偏偏這二科是最不受人重視的。
    在我讀在職時,同學都說我為何修那些"硬"學科,我說在職不就是要學"有用的",但一般人是修"好過的"。好學科是如何定義的,真的要好好想一下。

    回覆刪除
    回覆
    1. 關於AI (或生成式AI) 會不會對創造產生影響?就看每個人的經驗與
      思考方式吧。
      但人生對於生命或歲月的體驗是一種很難用簡單文字或影像表達的。
      他是一種非常深層的感情體會的。就像當我們再聽一位演講者或一位
      電影明星在詮釋一個故事時,也不全然可以從他的文字或肢體語言
      就可以傳達或接受的訊息。
      或許有一天這些生硬的AI 也可以做得到(科學家或工程師們所努力的目標)
      但就像有些人事物是無法完全被取代的啦。
      就像有些配偶或親子之間的那一種情感關係一樣。
      所以對我來說:面對許許多多的AI 浪潮帶來的衝擊,
      但我還是用一個很平常心來看待,人生不光只是科學或技術而已。
      而是每個人對於生命的用心體會過程:不管是甘甜苦辣,
      那是一種對生命、思想的尊重與修行啊。
      ---
      "好學科是如何定義的,真的要好好想一下。"
      同樣的,
      每個人對於自己一生的定義與註解是如何定義的,真的要好好想一下。

      感謝你的留言與見解,真的說得很好。

      刪除
    2. 看到之前網路一則留言很有感觸,當初說好AI出現是要幫我們洗碗跟掃地的,然後我們才有時間去創作詩歌等等去歌頌人生,但是現在AI出來之後我們還是要洗碗、掃地,然後AI創作詩歌......

      刪除
    3. @小邱 在這個社會或任何歷史時代洪流裡,
      總是有人覺得他可以左右或改變甚麼?
      但其實最後甚麼也做不了,結果還是回到一個在平凡不過的人而已。
      畢竟怎麼努力都還是擺脫不了自然生老病死的輪迴。
      只要不要過於期望甚麼?也就不會覺得失望了。
      做做洗碗掃地的工作,就把它當作一個生活態度吧。
      小時候,課本教我們灑掃應對進退的,我也覺得蠻不錯的啊。
      叫AI 做,他也未必懂得這個道理吧。

      刪除
  2. 當Playboy用AI生成時,突然就覺得沒有價值了,為何?
    這個時代,有些東西變得很容易得到時,價值就變了。
    價值是什麼?

    回覆刪除
    回覆
    1. 嗯...價值是什麼?
      別人靠AI 可以賺到錢,我不能,這個就是AI 的價值嗎?
      有人整天抱著技術不放,總覺得技術就是工程師一種神聖的價值。
      我說了:人生不是只有技術或工作而已。
      每個人在不同的角色扮演上,都有其不同的價值定義。
      整天沒日沒夜的工作賺錢,忽略家庭親友,就算你盆滿缽滿,
      就比較有價值嗎?
      或整天關起門來搞自己喜歡做的技術或產品,公司不賺錢?
      員工部屬沒前途未來?就覺得自己非常有價值了嗎?
      ---
      價值是來自於不同角度的詮釋,你自己覺得幹工程師很有價值,
      但老婆小孩整天抱怨看不到你,也不見你關心呵護。
      小公司搞個小玩意兒,就覺得天大地大沒有你偉大。
      在別人的眼中,卻是小打小鬧的格局,出不了廳堂賺錢獲利。
      你覺得有意義有價值,但人家創投卻嗤之以鼻,自己還以為:
      別人不懂得欣賞。
      所以呢?什麼是價值?
      你所提的:有些東西變得很容易得到時,價值就變了。
      對的。價值其實來自於投資報酬率,技術與人生都是如此:
      懂得犧牲,就會懂得收穫,自然就可以定義價值了。
      沒有人可以是完全百分之百的成功者的啦。

      刪除
    2. 何謂價值?它是開放式問題。
      分不同層次
      自我理想確定前:無法了解,最低價為主。或是開始計算投入及報酬,供需理解中。
      自我理想確定後:開始規劃如何達到理想,開始規劃及分析,計算投報率。發現別人的價值可能和自己不同。開始了解供需。
      開始推論價值:將別人的需求納入規劃,開始合作。開始了解供需的系統行為,開始找市場行為以了解不同人的需求。
      系統價值觀:了解價值是系統性行為,開始推論次系統行為模型並收集可能的模型。合作外又會找出更大的目標,進而規劃團隊合作方法。

      我一直在找去年我失敗的原因,我一直覺得前老闆的價值觀或是規劃和我有差距。現實的資金也使得他做出和我想的不同決定,個成員都有自己的想法及立場。團隊要如何運作?我看得到問題,卻沒有人認同,這個問題出在那裏?我總算確定問題在於價值觀的層次深度不對。
      前老闆只是確認自我目標,然後就拿錢去創業了。然後第一個產品就失掉市場。總算是有學到一點點要去思考別人的價值觀。還不是很確定是系統行為,然後第二輪開始,我出現了。資金出問題,再來團隊出問題,最後金主抽銀根,公司就收了

      刪除
    3. 其實懂得反思是一件很好的事,尤其是當年紀越來越大時。
      關於你提到去年老闆創業的故事,這樣子也很好啊。
      畢竟也懂得設下停損點....重新調整步調,重新出發。
      真的也不要一直沉迷不悟。
      有些失敗的故事或過程也是一個正面教育的價值。
      有些人雖然稱不上失敗。其實也是在低空環境裡,載浮載沉的。
      上了年紀,也看不到真正的理想目標,卻又捨不得放棄調整。
      是啊~是餓不死啊,但也真的到底是為了什麼?堅持什麼?
      可能就是你所說的:整個系統性的價值觀都得重新定義與分析。
      ---
      也希望你可以在未來的機會中,找到真正的價值。加油!

      刪除
    4. 我也是這件事了解到團隊的核心成員除了工作目標要一致外,價值觀也會影響到各成員的決策。價值觀差太多,連朋友都很難當,出差在一起都不想走一起。然後對問題理解的深度及價值觀的不同,在重要決策上就會分岐,最終團隊會很難朝向一方向。
      不過一開始合作,根本不知道這一個小小的差異,方向越偏越大。再加上若又不會向上管理,對金主沒有交代好,金主也要對各成員做試探,發現大家各有立場,又會加重不信任,就很難再投資。
      反省只是我不想浪費掉這次失敗,找出失敗最關鍵原因。我不想在相同類型的問題上再重蹈覆徹。

      刪除
    5. 所以我才說:公司經營就不光只是搞技術就好。
      有些小公司的主管(或是老闆,尤其是搞技術的老闆)總覺得:
      只要我老闆或公司有優於市場的技術,就是公司團隊共同的價值了。
      所有人事物的決策都是老闆說了算。但這樣的老闆真的就能搞定:
      業務市場、公司資金等運作決策嗎?
      其實道理很簡單: 老闆只要多花一點心思在技術,就會在其他
      管理決策方面少一點時間與心思。
      年輕時要當老闆,有很多事都要學,有些東西可以在大公司裡學,
      就像你所說的:向上管理,不一定可以馬上上手,
      但要知道有這麼一回事,隨著資源成長,組織規模也會變,
      當老闆就要懂得凝聚核心成員的工作目標,強化組織價值觀。
      也包括董事會的管理與共識。做不到,那就乾脆不要做了。
      寧願打掉重練,否則你所說的:不信任就無法再投資,
      包括:資金再投資或(研發)資源再投入等。
      你的想法與分析沒有錯,趕快找出失敗關鍵原因。
      不要在相同類型問題上重蹈覆轍。
      之前也看過許多技術主管帶著研發團隊另起爐灶的,
      不能怪技術團隊,為了找技術發揮的機會,也是不得不的選擇啊。
      這也是一種求生存之道,但就是要先搞定這些,才會有好的
      發展基礎啊。尤其是面對日新月異的技術挑戰所要具備的基礎條件。

      刪除