2023年1月4日 星期三

ATMEL SAM4S 實際應用

電路板V1

  • 開發板 Board 100 ( #39 )
  • 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 (最新被移除)。

開發板電源(左下方及中間)

  • 左下下方,左邊是AC/DC進入。
  • 左下下方,右邊是JTAG,右邊可以更進一步插入排線時,由上面正視最左邊是黑色, 另一端接USB。顯示DEBUG訊息,這是利用TX/RX,作法是,打開"設定",搜尋"裝置管理員",確認USB的項目是COM2,然後打開一個PUTTY,設定為COM2,38400,SERIAL,進行連線,可在畫面看到數值。
  • 軟體部分要設定Globals/GlobalSettings.h,設定為#define DEBUG_MESSAGE_OUTPUT    1。寫資料的方法是 PrintDebugString("\r\n########## -- Value = x"); PrintDebugU32Hex(value);( 這還有另外一個define )
  • JTAG用REMOTE CONTROL控制系統運作。(目前較少使用)
  • 左下方,下面是外部供電,上面是電池供電( 包括電池盒、一顆鋅空心 )。
    左方,有個SUPERCAP邊緣是要不要使用SUPERCAP( 目前是取消的狀況 )
  • 電源處理:中下DC/DC LDO


    • LDO,也是DC/DC的一種,線性穩壓器。Low Dropout的簡稱,是即使輸入輸出間電位差低,依然可以運作的線性穩壓器。亦稱作低損耗型線性穩壓器和低飽和型線性穩壓器。

INPUT(中下方)

中間下方兩個,分別是BUTTON 1 BUTTON 2
在APP程序中,同時按下,就會驅動模擬注射程序。(同時按下是考慮使用安全)
同時長按 => 聽到長回應,準備開始
同時短按連續三下 => 聽到短回應(三下)
同時短按,當作確認  => 聽到長回應,系統會依照剛剛輸入時間長度運轉馬達

PI SENSOR
板子中間區域有個小型PI SENSOR,PI的連結線有三個
1.輸出:系統電源,要單獨打開
2.輸出:PI光照電源,這是PWM,要設定頻率和開啟PWM
3.讀入:ADC數值

OUTPUT(中上)

BUZZLER
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。

GPIO等設定

kerneldriver.c
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);



  • aaaaaa
  • bbbbb
  • ccccc

實際演練加入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) 文件中

使用的線路


看起來是有兩組TWI,時序資料如下
從範例來看,有兩種版本
原始版本,聽說是atmel studio 6.1版本(以前?)系統配合有的範例目錄,就是上面提到的MAIN_MCU\Build_infrastruction\Firmware\Lib_Sam4s8b\include
A   比較簡單的範例是 連結
  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版本,做法應該類似 )參考範例(連結)
LIS3DSH 範例一   (比較有參考性)

#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);
*/  
LIS3DH  範例二  (比較沒有參考性)
 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

沒有留言:

張貼留言