使用Raspberry Pi 4B控制Amiccom RF IC做Basic T/RX Link (此篇使用A5133為例)

SamYang2024-10-10Linux49
  • 本篇介紹使用嵌入式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已經安裝完成

image.png

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

gpio -v

image.png

  • 安裝好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 PiAmiccom A5133 Module
MOSI(GPIO 10) OutputSDI Input
MISO(GPIO 9) InputGIO1 Output
SCK(GPIO 11) OutputSCK Input
CS(GPIO 8) OutputSCS Input

image.png

  • 接下來我們就需要將Amiccom 提供的51核Code,移植到RPI上面

  • 因為ubuntu上無法像在windows上可以安裝IDE的debug工具,建議可以準備台邏輯分析儀,可以同步觀察SPI的時序狀態

  • 我們需要安裝gcc 編譯器

sudo apt install gcc

安裝完成後一樣先看一下版本

gcc --version

image.png

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的方法,基本上就沒問題

  1. 把該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圖:

image.png

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圖:

image.png


    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");
}

image.png

LA圖:設GIO1 REG(Address=0x0B),Value=0x19,改成SDO Mode,RPI下0x46(Read 0x06Reg),MISO開始輸出8Byte ID

image.png

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

image.png

  • 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寫入的一致

  • RPI_MOSI.zip

image.png

LA圖:

image.png

    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圖:

image.png

    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;
}

image.png

  • Waiting IF,RSSI,RC Procedure

image.png

  • 當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

image.png

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

image.png

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

image.png

  • 若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 */

image.png

  • 設定FIFO長度,並下PLL CMD

	RF_FIFOLength(64-1);//64 bytes
	RF_StrobeCmd(CMD_PLL);

image.png

  • 設定RF中心頻率

RF_SetCH(80);//freq=5805.001MHz  80

image.png

  • 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,
};

image.png

  • 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;

image.png

  • 下CMD_TX,此時可以看到A5133的GIO2 Pin Go High

 RF_StrobeCmd(CMD_TX);

image.png

  • 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));


image.png

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








发表评论

访客

◎欢迎参与讨论,请在这里发表您的看法和观点。