使用Raspberry Pi 4B控制Amiccom RF IC做Basic T/RX Link (此篇使用A5133為例)
本篇介紹使用嵌入式Raspberry Pi安裝ubuntu系統,撰寫SPI Driver控制Amiccom A5133
安裝好ubuntu系統後,我們需要先安裝Raspberry Pi相關的GPIO Driver的C lib,網上有許多推薦的Source,Ex:wiringPi,pigpio,BCM2835等等都可以,重要只要能夠驅動Raspberry pi的GPIO SPI Write/Read即可,本篇選擇安裝wiringPi的GPIO C lib
安裝步驟:
先git 下載WiringPi的安裝包
git clone https://github.com/WiringPi/WiringPi.git
下載完後切換到此資料夾,然後開始安裝
cd WiringPi sudo ./build
看到All Done字樣,表示WiringPi的lib已經安裝完成

執行"gpio -v"可以查看安裝的wiringPi版本
gpio -v

安裝好WiringPi 後基本上,我們就已經取得了驅動RPI(Raspberry Pi)的SPI最基本的Write/Read驅動
請注意WiringPi的SPI是使用RPI硬件的4 Wire SPI,這裡我們選用SPI_0,因此我們需要將RPI上的SPI0_MOSI(GPIO 10),SPI0_MISO(GPIO 9),SPI0_SCLK(GPIO 11),SPI0_CS0(GPIO 8),拉出來對接至A5133的SPI接口
之前介紹過Amiccom 的RF IC都為Slave端,且預設是3 Wire SPI,若要改成4 Wire SPI就需要使用的A5133的GIO1改成SDO Mode
詳細接線圖:
| Raspberry Pi | Amiccom A5133 Module |
| MOSI(GPIO 10) Output | SDI Input |
| MISO(GPIO 9) Input | GIO1 Output |
| SCK(GPIO 11) Output | SCK Input |
| CS(GPIO 8) Output | SCS Input |

接下來我們就需要將Amiccom 提供的51核Code,移植到RPI上面
因為ubuntu上無法像在windows上可以安裝IDE的debug工具,建議可以準備台邏輯分析儀,可以同步觀察SPI的時序狀態
我們需要安裝gcc 編譯器
sudo apt install gcc
安裝完成後一樣先看一下版本
gcc --version

Demo code:
Raspberry Pi_ FIFO mode_0.5M_1M_2M_4Mbps_Colad9p_V0.7.zip
p.s.這邊v0.7版是follow Amiccom 提供的第0.7版Ref Code抄寫過去的
底下Demo code會一步一步的去對比邏輯分析儀的數據,來說明其實只要掌握住控制SPI Write/Read的方法,基本上就沒問題
把該Project會用到的.h檔都先加入進來
這邊需手動撰寫define.h,A5133reg.h,A5133Config.h,都按照Amiccom提供的Ref code照抄即可,其他的.h為系統夾帶的
#include "define.h" #include "A5133reg.h" #include "A5133Config.h" #include <wiringPi.h> #include <stdint.h> // 包含标准的整数类型定义 #include <wiringPiSPI.h> #include <stdio.h>
2.定義一個Flag_MASTER用來區分A5133要執行TX端或RX端,剩下的是定義給RX時計算封包量與誤碼量的變數
uint8_t Flag_MASTER = 1;//1=TX;0=RX uint16_t RxCnt,Err_Loss,Err_Frame; uint32_t Err_BitCnt; uint8_t tmpbuf[64];
3.定義Amiccom A5133會用到的ID CODE與Payload內容
const Uint8 BitCount_Tab[16] = {0,1,1,2,1,2,2,3,1,2,2,3,2,3,3,4};
const Uint8 ID_Tab[8]={0xC3,0x3C,0x95,0x6A,0x36,0x75,0xC5,0x2A};//ID code
//const Uint8 //code ID_Tab[8]={0x34,0x75,0xC5,0x2A,0xC7,0x33,0x45,0xEA};//ID code
//const Uint8 code ID_Tab[8]={0x55,0x55,0x55,0x55,0x34,0x75,0xC5,0x6A}; //ID2 code
//const Uint8 code ID_Tab[8]={0xFF,0xFF,0xFF,0xFF,0x36,0x75,0xC5,0xBA}; //ID2 code
//const Uint8 code ID_Tab[8]={0x34,0x75,0xC5,0x2A,0x34,0x75,0xC5,0x2A}; //ID2 code
const Uint8 KeyData_Tab[16]={0x00,0x00,0x00,0x00,0x00,0x0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}; //keyData code
const Uint8 FCB_Tab[20]={0x00,0x00,0x00,0x00,0x00,0x15,0x20,0x25,0x30,0x35,0x40,0x45,0x50,0x55,0x60,0x65,0x70,0x75,0x80,0x85}; //keyData code
const Uint8 PN9_Tab[]=
{ 0xFF,0x83,0xDF,0x17,0x32,0x09,0x4E,0xD1,
0xE7,0xCD,0x8A,0x91,0xC6,0xD5,0xC4,0xC4,
0x40,0x21,0x18,0x4E,0x55,0x86,0xF4,0xDC,
0x8A,0x15,0xA7,0xEC,0x92,0xDF,0x93,0x53,
0x30,0x18,0xCA,0x34,0xBF,0xA2,0xC7,0x59,
0x67,0x8F,0xBA,0x0D,0x6D,0xD8,0x2D,0x7D,
0x54,0x0A,0x57,0x97,0x70,0x39,0xD2,0x7A,
0xEA,0x24,0x33,0x85,0xED,0x9A,0x1D,0xE0,
};4. 定義一下接下來會用到的SPI Channel '0',& SPI 速度,A5133的MAX SPI data rate up to 10 Mbps,因為是basic FIFO資料都是緩存在RF IC Buf裡,所以我們這邊使用低速1Mbps即可,這邊RPI需要先定義一隻普通I/O pin,用來讀取A5133 GIO2 Output出來的logic變化,目的是確認RF狀態
#define SPI_CHANNEL 0 // SPI channel (0 or 1) #define SPI_SPEED 1000000 // SPI speed in Hz #define GIO2 7 // Define the GPIO 4 pin number (WiringPi numbering 7)
Main Code介紹
1.初始化I/O & 設定GIO2 為Input模式(對於RPI來說),設為下拉模式(Normal LOW)
2.初始化SPI部分
//INIT GPIO
if (wiringPiSetup() == -1) { // Alternatively, you can use wiringPiSetupGpio() if using BCM numbering
printf("Failed to initialize WiringPi\n");
return -1;
}
pinMode(GIO2, INPUT);//use check RF State
// Enable pull-down resistor
pullUpDnControl(GIO2, PUD_DOWN);
//INIT SPI
int fd = wiringPiSPISetup(SPI_CHANNEL, SPI_SPEED);
if (fd == -1) {
printf("Failed to initialize SPI\n");
} else {
printf("SPI initialized successfully\n");
}
delay(100);3. 若Flag_MASTER=1,進入TX端,執行初始化A5133 RF
if(RF_Init())/* init RF */ Err_State();
詳細初始化介紹:
1.對A5133 Reset,也就是對Address 0x00,寫入0x00 Value
Uint8 RF_Init(void)
{
Uint8 i;
Uint8 id[8] = {0};
RF_Reset();/* reset RF chip */LA圖:

void RF_Reset(void)
{
RF_WriteReg(MODE_REG, 0x00); //reset RF chip MODE_REG=0x00
}RF_WriteReg函數為2Byte SPI(1 Address+1Value)
將Address定義為data[0],Value定義為data[1]
調用 wiringPiSPIDataRW(SPI_CHANNEL, data, 2);將2 byte SPI送出
void RF_WriteReg(Uint8 addr, Uint8 dataByte)
{
uint8_t data[2];
data[0]=addr;
data[1]=dataByte;
printf("Sending data over SPI:Reg=0x%02X,Vaule=0x%02X,", data[0], data[1]);
int result = wiringPiSPIDataRW(SPI_CHANNEL, data, 2);
if (result == -1)
{
printf("Failed to send data over SPI\n");
}
else
{
// Print the received data
printf("Send Finish\n");
}
} 2. 寫ID code,這邊需注意Amiccom RF IC的SPI Format,都是一次性CS拉LOW後,一連貫的Address , Value寫完後CS才有拉High,而不是每寫一個Byte,CS就拉LOW/High一次,所以撰寫時需要注意
RF_WriteID(ID_Tab);/* write ID code */
這邊Address+8Byte的ID Code總共預留9Byte的數組空間
IDCODE_REG=0x06,指向data[0],後面ID內容為之前定義過的
const Uint8 ID_Tab[8]={0xC3,0x3C,0x95,0x6A,0x36,0x75,0xC5,0x2A};//ID code這邊將ID內容指向數組data[1]~data[8]
一次性的MOSI Output這9Byte資料給A5133
void RF_WriteID(const uint8_t *ptr)
{
Uint8 i;
uint8_t data[9];//1byte adderss & 8 byte ID Code
data[0]=IDCODE_REG;//address
for (i=0; i < 8; i++)
{
data[i+1]= *ptr;
ptr++;
}
printf("Sending data over SPI:Reg=0x%02X,Vaule=0x%02X,0x%02X,0x%02X,0x%02X,0x%02X,0x%02X,0x%02X,0x%02X", data[0],data[1],data[2],data[3],data[4],data[5],data[6], data[7],data[8]);
wiringPiSPIDataRW(SPI_CHANNEL, data, 9);
printf(",ID Send Finish\n");
}LA圖:

3.Read ID,此動作是為了確保ID是否真正寫入A5133,Write/Read ID的目的也是為了Check SPI的MOSI/MISO是否都能正常work。
由於是4線SPI,所以我們需要先將A5133的GIO1設為SDO mode,才能正確讀取A5133回傳的MISO訊號
RF_WriteReg(GIO1_REG, 0x19);
將SPI MISO輸出的ID CODE與預設的ID_Tab做一個比對,確認是否一致正確
RF_ReadID(id);
for (i=0; i<8; i++)
{
if (id[i] ^ ID_Tab[i])
return 1;/* fail */
}Read ID部分,一樣跟Write ID一樣,需定義好9Byte的buffer數組(1Byte Address+8Byte Value)
buffer[0]=IDCODE_REG | 0x40,Amiccom的RF IC Address | 0x40(bit6=1)的目的就是為了去Read此Address,詳細可以參考Amiccom 手冊SPI章節(bit6=0=write mode)/(bit6=1=read mode)
這邊我們需要先把buffer[1]~buffer[8]都先定一個Dummy Value(因為這是Wiringpi SPI Lib的寫法,其他驅動lib可能有所不同,請注意)
然後再執行wiringPiSPIDataRW(SPI_CHANNEL, buffer, 9);,這邊應該wiringPi的SPI是全雙工的,所以Read/Write可以在同一段code完成
我們可以將buffer[1]~buffer[8] print出來看看是否與寫入的一致
void RF_ReadID(Uint8* ptr)
{
uint8_t buffer[9];
buffer[0] =(IDCODE_REG | 0x40);
for (int i = 1; i < 9; i++)
{
buffer[i] = 0x00; // Dummy data
}
wiringPiSPIDataRW(SPI_CHANNEL, buffer, 9);
printf("Read ID Code=");
for (int i=1; i<9; i++)
{
*ptr++ = buffer[i];
printf("0x%02X ", buffer[i]);
}
printf("\n");
}
LA圖:設GIO1 REG(Address=0x0B),Value=0x19,改成SDO Mode,RPI下0x46(Read 0x06Reg),MISO開始輸出8Byte ID

4.Load Config,這邊需要注意,因為Amiccom原始提供的config GIO1並不是0x19 SDO,雖然一開始有手動修改,但這邊Load Config後,又會強制改回跟config.h一致,所以我們需要先去手動編輯A5133Config.h GIO1部分改成0x19,這樣load config後GIO1才回維持SDO Mode

Load Config部分只需要按Amiccom提供的Ref code照抄即可
void RF_Config(void)
{
Uint8 i;
printf("Load Config Start\n");
for (i=0x01; i<=0x04; i++)
RF_WriteReg(i, A5133_RFConfigTab_Main[i]);
for (i=0x07; i<=0x1F; i++)
RF_WriteReg(i, A5133_RFConfigTab_Main[i]);
for (i=0; i<=12; i++)//0x20 code1
RF_WritePage(0x20, A5133_RFConfigTab_Addr0x20[i], i);
for (i=0; i<=12; i++)//0x21 code2
RF_WritePage(0x21, A5133_RFConfigTab_Addr0x21[i], i);
for (i=0; i<=5; i++)//0x22 code3
RF_WritePage(0x22, A5133_RFConfigTab_Addr0x22[i], i);
for (i=0x23; i<=0x29; i++)
RF_WriteReg(i, A5133_RFConfigTab_Main[i]);
for (i=0; i<=12; i++)//0x2A DAS
RF_WritePage(0x2A, A5133_RFConfigTab_Addr0x2A[i], i);
for (i=0x2B; i<=0x35; i++)
RF_WriteReg(i, A5133_RFConfigTab_Main[i]);
RF_WriteReg(0x37, A5133_RFConfigTab_Main[0x37]);
for (i=0; i<=11; i++)//0x38 ROM
RF_WritePage(0x38, A5133_RFConfigTab_Addr0x38[i], i);
for (i=0x39; i<=0x3C; i++)
RF_WriteReg(i, A5133_RFConfigTab_Main[i]);
RF_WriteReg(0x3E, A5133_RFConfigTab_Main[0x3E]);
printf("Load Config Finish\n");
}
void RF_WritePage(Uint8 addr, Uint8 wbyte, Uint8 page)
{
RF_WriteReg(RFANALOG_REG, (A5133_RFConfigTab_Main[0x35]&0x0F) | page<<4);
RF_WriteReg(addr, wbyte);
}這邊可以使用LA自帶軟件輸出SPI 封包csv檔,即可快速對比資料是否與A5133Config.h寫入的一致

LA圖:

5.RF_TrimmedValue_Init();/* load trimming value */
此部分照Amiccom Ref Code照抄即可,為cal RF的一部分
void RF_TrimmedValue_Init(void)
{
Uint8 i;
Uint8 trimValue[8];
//Uint8 tmp_checksum;
//trimValue[0]=FBG
//trimValue[1]=CTR
//trimValue[2]=BDC
//trimValue[3]=STM
//trimValue[4]=Checksum for trimvalue[0]~trimvalue[3]
//trimValue[5]=CSXTL
//trimValue[6]=FBG_CP
//trimValue[7]=Checksum for customer
RF_WritePage(ROMP_REG, A5133_RFConfigTab_Addr0x38[9] | 0xA0, 9);//enable EFSW=1, EFRE=1
delay(5); //wait for stability
Uint8 buffer[9]; // Total 9 bytes (1 byte to send + 8 bytes to read)
buffer[0] = USID_REG | 0x40; // Example: 1-byte command to send
for (i = 1; i < 9; i++)
{
buffer[i] = 0x00; // Fill the rest with dummy data (zeros)
}
wiringPiSPIDataRW(SPI_CHANNEL, buffer, 9);
for (i = 1; i < 9; i++)
{
trimValue[i-1]=buffer[i];
}
RF_WritePage(ROMP_REG, A5133_RFConfigTab_Addr0x38[9], 9);//disable EFSW=1, EFRE=1
if((trimValue[0] + trimValue[1]) == trimValue[4]) //case1-only FT
{
if((trimValue[0]!=0) && (trimValue[1]!=0))
{
RF_WritePage(ROMP_REG, (A5133_RFConfigTab_Addr0x38[1] & 0xE0) | trimValue[0], 1);//FBG
RF_WritePage(ROMP_REG, (A5133_RFConfigTab_Addr0x38[2] & 0xC0) | trimValue[1], 2);//CTR
}
else
Err_State();
}
else if((trimValue[0] + trimValue[1] + trimValue[2] + trimValue[3]) == trimValue[4]) //case2-CP+FT
{
if((trimValue[0]!=0) && (trimValue[1]!=0) && (trimValue[2]!=0) && (trimValue[3]!=0))
{
RF_WritePage(ROMP_REG, (A5133_RFConfigTab_Addr0x38[1] & 0xE0) | trimValue[0], 1);//FBG
RF_WritePage(ROMP_REG, (A5133_RFConfigTab_Addr0x38[2] & 0xC0) | trimValue[1], 2);//CTR
RF_WritePage(ROMP_REG, (A5133_RFConfigTab_Addr0x38[0] & 0x03) | (trimValue[2]<<2), 0);//BDC
RF_WritePage(ROMP_REG, (A5133_RFConfigTab_Addr0x38[4] & 0x40) | trimValue[3], 4);//STM
}
else
Err_State();
}
else //only CP
{
if((trimValue[0]==0) && (trimValue[1]!=0) && (trimValue[2]!=0) && (trimValue[3]!=0) && (trimValue[4]==0) && (trimValue[6]!=0))
{
RF_WritePage(ROMP_REG, (A5133_RFConfigTab_Addr0x38[1] & 0xE0) | trimValue[6], 1);//FBG
RF_WritePage(ROMP_REG, (A5133_RFConfigTab_Addr0x38[2] & 0xC0) | trimValue[1], 2);//CTR
RF_WritePage(ROMP_REG, (A5133_RFConfigTab_Addr0x38[0] & 0x03) | (trimValue[2]<<2), 0);//BDC
RF_WritePage(ROMP_REG, (A5133_RFConfigTab_Addr0x38[4] & 0x40) | trimValue[3], 4);//STM
}
else
Err_State();
}
}LA圖:

6.RF Calibration
按Amiccom Ref Code照抄即可,RF_Cal流程為固定流程,若出現校準不過,需先檢查模塊HW,請勿任意修改門檻數值,任意修改可能會造成後續射頻出現問題
Uint8 RF_Cal(void)
{
Uint8 tmp;
Uint8 rhc,rlc,fb,fbcf,fcd;
RF_StrobeCmd(CMD_PLL); //calibration @PLL state
RF_WriteReg(RFANALOG_REG, 0);
//IF,RSSI,RC procedure
RF_WriteReg(CALIBRATION_REG, 0x23);
do{
tmp = RF_ReadReg(CALIBRATION_REG)&0x23;
}while(tmp);
//calibration VBC,VDC procedure
if(RF_Cal_CHGroup(20)) //calibrate channel group Bank I
return 1;
if(RF_Cal_CHGroup(60)) //calibrate channel group Bank II
return 1;
if(RF_Cal_CHGroup(100)) //calibrate channel group Bank III
return 1;
RF_StrobeCmd(CMD_STBY); //return to STBY state
//for check
tmp = RF_ReadReg(IFCAL1_REG);
fb = tmp & 0x0F;
fbcf = (tmp>>4) & 0x01;
tmp = RF_ReadReg(IFCAL2_REG);
fcd = tmp & 0x1F;
rhc = RF_ReadReg(RXGAIN2_REG);
rlc = RF_ReadReg(RXGAIN3_REG);
Uint8 Mem_RH = rhc;
Uint8 Mem_RL = rlc;
if(fbcf)
return 1;//error
return 0;
}
Uint8 RF_Cal_CHGroup(Uint8 ch)
{
Uint8 tmp;
Uint8 vb,vbcf,vcb,vccf;
Uint8 deva,adev;
RF_WriteReg(PLL1_REG, ch);
RF_WriteReg(CALIBRATION_REG, 0x1C);
do{
tmp = RF_ReadReg(CALIBRATION_REG)&0x1C;
}while (tmp);
//for check
tmp = RF_ReadReg(VCOCCAL_REG);
vcb = tmp & 0x0F;
vccf = (tmp>>4) & 0x01;
tmp = RF_ReadReg(VCOCAL1_REG);
vb = tmp & 0x0F;
vbcf = (tmp >>4) & 0x01;
tmp = RF_ReadReg(VCODEVCAL1_REG);
deva = tmp;
tmp = RF_ReadReg(VCODEVCAL2_REG);
adev = tmp;
if(vbcf)
return 1;//error
return 0;
}
Waiting IF,RSSI,RC Procedure

當Read CALIBRATION_REG0x02 為0x00時,表示IF,RSSI,RC 皆cal pass
並接下去運行RF_Cal_CHGroup,分別CH20 calibrate channel group Bank I,CH60 calibrate channel group Bank II,CH100 calibrate channel group Bank III

Cal完全部CHGroup後,下一個standby cmd=0xA0

for Check fb,fbcf,fcd,rhc,rlc Value

若fbcf不等於1,表示cal pass,若等於1,表示cal fail,需找出cal fail的原因
7.進入RF部分
首先設定GIO1,GIO2,CKO的相應Mode
RF_WriteReg(GIO1_REG,0x19);/* gio1-SDO */ RF_WriteReg(GIO2_REG,0x01);/* gio2-WTR */ RF_WriteReg(CKO_REG,0x02);/* cko-dck */

設定FIFO長度,並下PLL CMD
RF_FIFOLength(64-1);//64 bytes RF_StrobeCmd(CMD_PLL);

設定RF中心頻率
RF_SetCH(80);//freq=5805.001MHz 80

write data to tx fifo buffer(0x05)
Data為PN9_Tab內容,0xFF,0x83,0xDF,0x17,0x32,0x09,0x4E,0xD1...........
const Uint8 PN9_Tab[]=
{ 0xFF,0x83,0xDF,0x17,0x32,0x09,0x4E,0xD1,
0xE7,0xCD,0x8A,0x91,0xC6,0xD5,0xC4,0xC4,
0x40,0x21,0x18,0x4E,0x55,0x86,0xF4,0xDC,
0x8A,0x15,0xA7,0xEC,0x92,0xDF,0x93,0x53,
0x30,0x18,0xCA,0x34,0xBF,0xA2,0xC7,0x59,
0x67,0x8F,0xBA,0x0D,0x6D,0xD8,0x2D,0x7D,
0x54,0x0A,0x57,0x97,0x70,0x39,0xD2,0x7A,
0xEA,0x24,0x33,0x85,0xED,0x9A,0x1D,0xE0,
};
RF_Low Voltage Reset_Check,此Function是確保A5133處於正常運作電壓1.8V以上,若<1.8V VCOCAL2_REG讀出來數值0xFF,則return 1重新進入RF_Init重跑一次
tmp = RF_ReadReg(VCOCAL2_REG); if(tmp == 0xFF)//default reset value 0xFF return 1;

下CMD_TX,此時可以看到A5133的GIO2 Pin Go High
RF_StrobeCmd(CMD_TX);

delay 10us waiting TX Modulation setting
While Wait GIO2(WTR) GO low,表示成功發送完1包TX Data
重新Reset TX FIFO Point,寫入下一包封包FIFO Data to TX FIFO Buffer(Address:0x05)
delayMicroseconds(10); // 10 microseconds delay while(digitalRead(GIO2));

到此我們已經完成使用Raspberry Pi撰寫SPI Write/Read A5133成功控制發送TX Data