電路板V1
- MCU board110 (SAM4S8B)
- ICE為 ATMEL SAM-ICE ( 排線有圈圈有顏色那端,靠近下方,由上面正視 )
- PC軟體為IAR Embedded Workbench IDE
- CODE在IAR完成後,透過ICE送進MCU。
- RESET MCU
- 右下方有個RESET按鈕,按下PCB右邊LED燈會有反應。
- 系統重啟。
- RESET MCU
- 拔除電源和ICE連結
- 連結MCU版面上右下方的CONNECTOR
- 通電,等待五秒鐘
- 拔除電源,拔除右下方的CONNECTOR,放在一邊
- 板上SAFETY MCU (最新被移除)
- SAFETY MCU會控制MOTOR開關。
- 如果要跳過SAFETY MCU控制MOTOR開關,下圖跳線方式,是直接將PCB上電源直接拉到MOTOR電源開關,等於設定ON。這種連結法,會持續浪費2mA左右電源。
- 第二種方式,MAIN MCU JP9(MCU內側),連結到SAFETY MCU JP97(MCU 外側),這種方式不會浪費電源。
- SAFETY MCU也會控制BUZZLER開關。
- 直接控制BUZZLER的方式,JPR8-NO(左側),連結到SAFETY MCU JP99(MCU 外側)。
- 版上BLE MODULE (最新被移除)。
開發板電源(左下方及中間)
中間下方兩個,分別是BUTTON 1 BUTTON 2
在APP程序中,同時按下,就會驅動模擬注射程序。(同時按下是考慮使用安全)
同時長按 => 聽到長回應,準備開始
同時短按連續三下 => 聽到短回應(三下)
同時短按,當作確認 => 聽到長回應,系統會依照剛剛輸入時間長度運轉馬達
PI SENSOR
板子中間區域有個小型PI SENSOR,PI的連結線有三個
1.輸出:系統電源,要單獨打開
2.輸出:PI光照電源,這是PWM,要設定頻率和開啟PWM
3.讀入:ADC數值
LED
透過4 PIN,打開白色在上面,四個電極接腳向下。
也可以用PIN拉到其他步進馬達。DC馬達(空心杯也相同),使用2022/12/27版本軟體,拉線方式如圖。
開發板V2
從MCU上面向下看,上方為BUZZER和MOTOR,右上方為TX RX區域。
TX RX PC端的線上面黑色為GND,由GND向下數,倒數第二為黃( 接到右上方的最右邊) 倒數第三為橘( 接到右上方的右邊數來第二個 )
PUTTY 38400 serial COMX。MOTOR最外面,接到馬達負極。
實驗的電池是501218 廠商說80mah 網路上看到65mah
JTAG的連線方式,先拔掉JTAG端,和電源。
測試連結正確性,然後調整電表,轉到檢測連通的部分,然後按最右邊的那個按鈕
正的探針,連結GND,負的探針依序連到每一個(六個)的接觸點,都要聽到聲音才行。
JTAG連結到PC那端,JTAG編號一那個部分,連結到PC端一。
連線順序,JTAG和PCB的對應方式是,以PCB右上方為第一個,對應到的是JTAG的第二個,2 6 1 5 3 4。JTAG最後一個是GND。
開發環境 IAR
IAR是單一軟體架構下,不同MCU就有不同安裝檔案。配合硬體安裝 IAR for ARM 7.10.1(HAPPY)。解開IAR for ARM 7.10.1\IAR_EWARM_710 安裝畫面選擇Install IAR Embedded Workbench 然後就是一路裝下去。抽屜有Dongle但占用USB太麻煩,選擇打開EWARM_7.10_License,依照說明把Selected.package放在C:\ProgramData\IARSystems\LicenseManagement\LicensePackages\ARM\EW\1\ (If the path doesn't exist, create it manually.) 打開MAIN_MCU_20230502 \ MAIN_MCU \ Build_infrastruction \ Firmware
IAR製做空專案可以參考(連結)
執行download and debug,updating build tree,系統會自動檢查專案中各程式碼是否有被更新,若有就compile,最後打包送入MCU。因為很少用debug功能,都是直接GO。後續把ICE拔開,系統重開後也會進入運作程序(這是我要的)。
IAR之外,atmel也有提供開發軟體AtmelStudio(7.0),可自動產生出driver。
Kernel_SetCPUPowerUpPinState
SetCPUPowerSavingPinState (); //configure PortA, PortB, PortC as GPI Pull-Up
把功能轉換 例如ERASE和另外..
MATRIX->CCFG_SYSIO |= (CCFG_SYSIO_SYSIO10|CCFG_SYSIO_SYSIO11);
設定CLK
PMC->PMC_PCER0 = PMC_PCER0_PID11|PMC_PCER0_PID12|PMC_PCER0_PID13;
設定pull high
PIOA->PIO_PPDDR = 0xFFFFFFFFUL;
PIOB->PIO_PPDDR = 0xFFFFFFFFUL;
PIOC->PIO_PPDDR = 0xFFFFFFFFUL;
PIOA->PIO_PUER = 0xFFFFFFFFUL;
PIOB->PIO_PUER = 0xFFFFFFFFUL;
PIOC->PIO_PUER = 0xFFFFFFFFUL;
PIOA->PIO_ODR = 0xFFFFFFFFUL;
PIOB->PIO_ODR = 0xFFFFFFFFUL;
PIOC->PIO_ODR = 0xFFFFFFFFUL;
PIOA->PIO_PER = 0xFFFFFFFFUL;
PIOB->PIO_PER = 0xFFFFFFFFUL;
PIOC->PIO_PER = 0xFFFFFFFFUL;
// Configure pins 整個的對應表放入
PIO_Configure(PowerUp_Pins, PIO_LISTSIZE(PowerUp_Pins));
//Enable Pull-down Register 部分拉回LOW
PIOA->PIO_PPDER = PIO_PA11 | PIO_PA12 | PIO_PA26;
kerneldriver.h
SW設計
下面是LAMINGO為主,搭配後來的WIPOC調整。
APPLICATION_LAYER
MAINLOOP
MAINLOOP.C 真正執行的位置在REMOTE CONTROL裡面,這裡就是去執行這個REMOTE CONTROL FUNCTION,而且是啟動後一秒鐘之後才開始,使用YH
DRIVER
HARDWAREDRIVER
ADCDRIVER YH改了很多,但都是配合新的PCB調整,使用原本
IDSDRIVER YH改了LED燈部分,為了SYNC,使用YH
KERNELDRIVER 整個MAPPING,兩個不同PCB這裡差異最大,使用原本
LED部分,先用WIPOC的概念
MOTOR部分,搭配MOTOR的啟動,有兩個新項目,SOLOPB也要拉線
GLOBAL
VERSION YH改了一點,使用原本
SERVICE_LAYER
COMMANDINTERPRETER
CommandExecutor YH借用,修改部分,使用YH
BLESleepMode 呼叫了Controller2_Button1Pressed
Controller2.c YH沿用原本APP,並且加入新流程,使用YH但要修改
Controller2_Button1Pressed
OneFunction4Demo
Controller2_fkt.h YH沿用,使用YH
EEPROM
二進制不同,不重要
IDSENGINE
IDSENGINE.c YH小修改,使用YH
KERNEL
INTERRUPT YH修改,對應按鈕功能等等,使用原本
POWERMANAGEMENT
POWERMANAGER YH將ifdef等等重新整理,但功能沒特別改變,使用原本
SYSTEMSTATE
SYSTEMSTATE 使用YH
SYSTEMTIME
RTC_RTT 雖然目前修改沒有實際用處,使用YH
USERINTERFACE
ILED_evtdef 使用YH
ISLD_evtdef 使用YH
KEYBOARD 使用原本
LED 使用YH
EVENT DRIVEN
目前本體是event driven但是,實驗實驗是用個大的寫死的function控制全部流程。這部分要繼續修改。
測試機制
因為是event driven,所以理論上是可以方便測試的,但還沒有實際組合起來。可以避開上面的UI,直接用C#寫對應的virtual function測試程式。同時還包括YH提到要修改的LIB等等。
UART原理
UART的程式位置在XXXX,透過TX RX溝通。
UART應用
UART應用有兩組,通訊組UART0:當成Console或是DVT測試(前面提到的測試機制)。程式範例 Source\Service_Layer\CommunicationDrivers\SlimIrDA
藍芽組:UART1。程式範例 Source \Service_Layer \CommunicationDrivers \BLE。配合event driven結構IBLEDriver_evt,實際連結是BLEDriver
- timer:StartTimerOfSendData StopTimer
- 主要流程:BLEDriver_ModuleEventHandler
- IBLEDriverEvent_eBLEModuleConnectRequest BluetoothLE_SendConnectRequest
while(1)
{
if(BluetoothLE_BLEUartTxFrame((uint8_t)BLECMD_CONNECT, (uint8_t*)NULL, (uint8_t)0))
break;
}
這個的細部功能是
放好表頭
g_mBLEBgHandleData[ 0] = BLE_UART_HEADER;
g_mBLEBgHandleData[ 1] = BLE_UART_HEADER_XX;
g_mBLEBgHandleData[ 2] = 3 + nDataLength;
g_mBLEBgHandleData[ 3] = nCommand;
拷貝資料
memcpy(&g_mBLEBgHandleData[ 4], pnData, nDataLength);
處理checksum
for(i=0; i<(4 + nDataLength); i++)
{
nCheckSum += g_mBLEBgHandleData[i];
}
nCheckSum = 0xFFFF - nCheckSum;
g_mBLEBgHandleData[4 + nDataLength] = (uint8_t)(nCheckSum & 0x00FF); // BLE-Uart CheckSum
g_mBLEBgHandleData[5 + nDataLength] = (uint8_t)((nCheckSum >> 8 ) & 0x00FF); // BLE-Uart CheckSum
塞好結構
g_mBLEUartStructTx.nTxLength = 6 + nDataLength;
g_mBLEUartStructTx.pnTxPointer = &g_mBLEBgHandleData[0];
BluetoothLE_UartTxFrame((uint8_t*)&g_mBLEUartStructTx);
真正和UART溝通,後面是call back
UARTDriver_SetTransmitCallbackFunction (UartInterface_eUart1, BLE_Callback_TXC);
這邊帶入的是UART1 (BLE) 就回到前面寫的
- IBLEDriverEvent_eBLEModuleSendDataWithoutACK BluetoothLE_SendData
if ((mg_nLastRawCmd == RAWCMD_SPEC_bGRECORD) || (mg_nLastRawCmd == RAWCMD_ALL_bGRECORD))
{
if (CurrentNumberofBgRecord >= 0)
{
mg_TransferDataAfterRawAck = bTRUE;
IBLEDriver_AllocateBLEModuleTransferRawDataEvent(ModuleId_eBLEDriver, ModuleId_eBLEDriver);
}
}
- IBLEDriverEvent_eBLEModuleReceiveFrameComplete memcpy(&anTempBuf, &anUartBuf, 20); BLE_CommandParser();
- IBLEDriverEvent_eBLEModuleRawDataParsing StopTimer (); memcpy(&anRawBuf, &anTempBuf, 20); BLE_HostCommandParser();
- IBLEDriverEvent_eBLEModuleTransferRawData StartTimerOfSendData BLE_TransferDataToHost
BLE_TransferDataToHost
BluetoothLE_SendBLEUart_BgNumberOfResult
while(1)
{
if(BluetoothLE_BgHandleTxFrame((uint8_t)BGCMD_TRANSFER_NUM_BG, (uint8_t*)&nNumberOfResult, (uint8_t)2))
break;
}
BluetoothLE_SendBLEUart_BgSpecifiedResult
while(1)
{
if(BluetoothLE_BgHandleTxFrame((uint8_t)BGCMD_TRANSFER_BG_RESULT, pResultData, (uint8_t)8))
break;
}
pData[0] = BG_HANDLE_HEADER;
pData[1] = nCommand;
pData[2] = nLength;
memcpy(&pData[3], pnData, nLength);
pData[3+nLength] = BG_HANDLE_TAIL;
BluetoothLE_BLEUartTxFrame((uint8_t)BLECMD_RAWDATA, pData, nLength+4);
實際演練加入I2C
1.從atmelstudio對應位置拿出需要的檔案,放在目前架構中。確認編譯OK
atmel系統中,不叫i2c,叫做Interface for configuration the Two Wire Interface (TWI) peripheral.
位置在MAIN_MCU\Build_infrastruction\Firmware\Lib_Sam4s8b\include。拷貝到應該的目錄,用add files的方式放進去,但才發現,其實原本專案已經有這些。
2.系統連結
從data spec來看
Datasheet\MainProcessor\ATMEL SAM4S(20140527) 文件中
原始版本,聽說是atmel studio 6.1版本(以前?)系統配合有的範例目錄,就是上面提到的MAIN_MCU\Build_infrastruction\Firmware\Lib_Sam4s8b\include
pTwi->TWI_PTCR = UART_PTCR_RXTDIS | UART_PTCR_TXTDIS;
TWI_ConfigureMaster(pTwi, TWI_CLOCK, VARIANT_MCK);
TWI_StartRead(pTwi, ADDR_B, TOBJ_1, 1);
lB = readByte();
hB = readByte();
TWI_SendSTOPCondition(pTwi);
pec = readByte();
uint8_t readByte() {
while (!TWI_ByteReceived(pTwi))
;
return TWI_ReadByte(pTwi);
}
找不到實際使用範例,決定放棄
B twi之外,目錄還有twid,挑選常用function,TWID_Read temperature,在github中尋找有兩種資料
像是MAIN的
部分 像是運作的
部分 很單純,沒有任何init的動作就開始運作( 哪一組則是另外找了範例 TWID_Initialize(&twid, TWI0); )
繼續找了實際用LIS3DSH的範例(
連結 實際產品範例
連結 ),感覺搭湊起來還有個樣子
後面程式會用這個方向繼續下去,不是YH提供的方法
C 過程中YH在atmel studio 7.0附屬的檔案中發現有另外一組程式
asf-standalone-archive-3.52.0.113\xdk-asf-3.52.0\sam0\drivers\i2c
我們使用的應該是master( not slave ) not interrupt ( 所以會是polling 也就是i2c_master.c )
現在沒有往這個方向找,這份文件應該就是討論這組內容(
連結)
3.加入APP控制.h中的檔案
硬體結構,只要兩條線。第一組部分線路被占用,直接使用第零組。
PA3 JP36 (右邊) DATA
PA4 JP56 (右邊) CLK
有設定的方式
這兩行是新加上去的
#define PIN_NA_PA3 {PIO_PA3, PIOA, ID_PIOA, PIO_PERIPH_A, PIO_PULLUP}
#define PIN_BLE_INT_MCU {PIO_PA4, PIOA, ID_PIOA, PIO_PERIPH_A, PIO_PULLUP}
流程可能是下面( ARDUINO版本,做法應該類似 )參考範例(
連結)
#define LIS3DSH_CTRL_REG3 0x23
#define LIS3DSH_CTRL_REG4 0x20
#define LIS3DSH_CTRL_REG5 0x24
#define LIS3DSH_CTRL_REG6 0x25
#define LIS3DSH_OUT_TEMP 0x0C
#define LIS3DSH_OUT_X_L 0x28
#define LIS3DSH_OUT_X_H 0x29
#define LIS3DSH_OUT_Y_L 0x2A
#define LIS3DSH_OUT_Y_H 0x2B
#define LIS3DSH_OUT_Z_L 0x2C
#define LIS3DSH_OUT_Z_H 0x2D
#define LIS3DSH_FIFO_CTRL_REG 0x2E
/*arduino example lis3dsh
// 0x0F = 0b00001111
// Normal power mode, all axes enabled, 50 Hz ODR
writeReg(LIS3DSH_CTRL_REG4, 0x5F);
// 200 Hz antialias filter, +/- 2g FS range
writeReg(LIS3DSH_CTRL_REG5, 0x80);
// configure FIFO for bypass mode
writeReg(LIS3DSH_FIFO_CTRL_REG, 0);
// disable FIFO, enable register address auto-increment
writeReg(LIS3DSH_CTRL_REG6, 0x10);
*/
5 #include <Wire.h>
7 #define ADDRESS_LIS3DH 0x19
8 #define CTRL_REG1 0x20
9 #define CTRL_REG4 0x23
10 #define CTRL_REG5 0x24
11 #define STATUS_REG 0x27
12 #define OUT_X_L 0x28
13
14 byte buffer[6];
15 byte statusReg;
16
17 boolean ready = false;
18 int outX, outY, outZ;
19 int xVal, yVal, zVal;
20
21 void setup()
22 {
23 Wire.begin();
24 Serial.begin(9600);
25 delay(5); //5 ms boot procedure
26
27 // reboot memory content, to make a clean start
28 Wire.beginTransmission(ADDRESS_LIS3DH);
29 Wire.write(CTRL_REG5);
30 Wire.write(0x80);
31 Wire.endTransmission();
32
33 delay(5);
34
35 //set ODR = 1 Hz, normal mode, x/y/z axis enabled
36 Wire.beginTransmission(ADDRESS_LIS3DH);
37 Wire.write(CTRL_REG1);
38 Wire.write(0x17);
39 Wire.endTransmission();
40
41 //set BDU= 1, scale = +/-2g, high resolution enabled
42 Wire.beginTransmission(ADDRESS_LIS3DH);
43 Wire.write(CTRL_REG4);
44 Wire.write(0x80);
45 Wire.endTransmission();
46 }
47
48 void loop()
49 {
50 // read STATUS_REG
51 while(ready == false)
52 {
53 Wire.beginTransmission(ADDRESS_LIS3DH);
54 Wire.write(STATUS_REG);
55 Wire.endTransmission();
56 Wire.requestFrom(ADDRESS_LIS3DH, 1);
57 if (Wire.available() >= 1)
58 {
59 statusReg = Wire.read();
60 }
61 if (bitRead(statusReg, 3) == 1) //new data available
62 {
63 ready = true;
64 }
65 delay(10);
66 }
67
68 if (bitRead(statusReg, 7) == 1)
69 {
70 Serial.println("Some data have been overwritten.");
71 }
72
73 //read the result
74 Wire.beginTransmission(ADDRESS_LIS3DH);
75 Wire.write(OUT_X_L | 0x80); //read multiple bytes
76 Wire.endTransmission();
77 Wire.requestFrom(ADDRESS_LIS3DH, 6);
78 if (Wire.available() >= 6)
79 {
80 for (int i = 0; i < 6; i++)
81 {
82 buffer[i] = Wire.read();
83 }
84 }
85
86 //calculation
87 outX = (buffer[1] << 8) | buffer[0];
88 outY = (buffer[3] << 8) | buffer[2];
89 outZ = (buffer[5] << 8) | buffer[4];
90 xVal = outX / 16;
91 yVal = outY / 16;
92 zVal = outZ / 16;
93
94 Serial.print("outX: "); Serial.print(xVal); Serial.print(" ");
95 Serial.print("outY: "); Serial.print(yVal); Serial.print(" ");
96 Serial.print("outZ: "); Serial.println(zVal);
實際演練設定GPIO
要控制五個GPIO,對應表如下
PA27 JP23 ERASE
PA28 JP52 XX28
PA29 JP49 XX29
PA30 JP41
PB13 JP46
程式架構上,原本event driven之外,還有service觀念。
ModuleList.c
{ModuleId_eKeyboard, Keyboard_ModuleSetup, Keyboard_ModuleEventHandler, Keyboard_ModuleSyncEventHandler},
keyboard.c
Keyboard_ModuleSetup
UserKeysInterruptCallback
UserKeysPinChangeCallback
Key12PressedCallback
Key12HeldCallback
TimerCallback4Demo
interrupt,入口是
KernelDriver.h
//PA15,GPI,BUTTON_SW
#define PIN_BUTTON_SW {PIO_PA15, PIOA, ID_PIOA, PIO_INPUT, PIO_PULLUP} //Falling Edge, L(0) is pressed.
//PA2,GPI,VIAL_SW (Vial Locker)
#define PIN_VIAL_SW {PIO_PA2, PIOA, ID_PIOA, PIO_INPUT, PIO_PULLUP} //Falling Edge, L(0) is pressed.
//PA14,GPI,BODY_SW (Body Sensor)
#define PIN_BODY_SW {PIO_PA14, PIOA, ID_PIOA, PIO_INPUT, PIO_PULLUP} //Falling Edge, L(0) is pressed.
//PA5,GPO,DUMMY_EN (Dummy Load)
interrupt.c
放入Key_Pins
要先enable
Interrupt_EnableExternalInterruptPortAKeyBoard 注意還有Interrupt_DisableExternalInterruptPortAKeyBoard
這種結構可以寫成這樣
PIOA->PIO_AIMER = PIO_PA15|PIO_PAXX;
PIOA_IrqHandler 這是系統原始設定,會進入的點
ExecuteInterrupt(InterruptVector_eExternalPortAKeyBoard) ; 從這裡就會進入到上面service