STM32 RDS解碼

       RDS(Radio Data System)無線數據廣播系統是在調頻廣播發射信號中利用副載波把電臺名稱、節目類型、節目內容及其它信息以數字形式發送出去。通過具有RDS功能的調諧器就可以識別這些數字信號,變成字符顯示在顯示屏上。在收到節目的同時,通過RDS可知道接收到的是那個電臺,它的發射頻率,並給出該電臺其餘的頻率,由此再使用“切換頻率”鈕來保證所接收的信號爲最強的頻率。RDS無線數據廣播文件可顯示接收到的節目名稱及其它資料。RDS功能可按節目類型決定取捨,尋找到符合你要求的電臺。RDS還能用來自動控制接收機,使流動工作的汽車收音機一直保持最佳接收狀態,及時收到緊急交通報告,有利交通安全。

       最近需要在我們的車載設備中集成RDS功能,這裏需要用到一個專門的RDS解碼芯片(這裏使用ST的TDA7478), 通過RDS解碼芯片,可以將空中的RDS廣播信號解調出來輸出給STM32,共有3路輸出,分別是QUAL(信號質量)、RDCL(時鐘信號)、RDDA(RDS數據)輸出,STM32在RDS時鐘到來是讀取RDS數據並存入到buffer中,當buffer中的數據校驗成功時,認爲收到正確的RDS數據。

       STM32具體操作流程如下:

1.配置QUAL、RDCL、RDDA爲輸入端口,並設置RDCL爲上升沿中斷;

2.在RDCL產生中斷時,讀取RDDA數據,並將數據存入到buffer中;

3.每收到一塊(26位)RDS數據後,做一次同步校驗,若同步失敗,則繼續做同步,若同步成功,則繼續接收下一塊數據;

4.當接收到連續的ABCD四塊數據並校驗無誤後,將四塊數據拼接成一組,發送給主CPU做進一步的解析處理(需參考RDS標準,如國標、歐洲標準,美洲標準)。

參考代碼如下:

//********************************************************************
//filename:  RDS.cpp
//created: 2012-04-24
//author:  firehood
//purpose: RDS解碼
//*********************************************************************
#include "RDS.h"
// 同步校驗碼(由偏置字*H矩陣計算得到)
const static int32u SYNCODE_A  = 0x03D8;
const static int32u SYNCODE_B  = 0x03D4;
const static int32u SYNCODE_C  = 0x025C;
const static int32u SYNCODE_C2 = 0x03CC;
const static int32u SYNCODE_D  = 0x0258;

static Boolean g_bRDSExist = FALSE;    // RDS是否存在
static Boolean g_bRDSEnable = FALSE;   // RDS功能使能標誌
static BLOCK   g_block = 0;			   // RDS數據塊
static BLOCK   g_blockA = 0,g_blockB = 0,g_blockC = 0,g_blockD = 0;
static int32u  g_bRevBitCount = 0;	   
static Boolean g_bSyncFlag = FALSE;    // 是否同步標誌
static Boolean g_nSyncLevel = 0;       // 同步級數
static Boolean g_bGroupFlag = FALSE;
static int8u   g_RDSGroup[16] = {0};   // 每組信息包含A、B、C、D四塊;
                                       // 每塊信息佔26bit,以四個字節存放(存在低26位) 

// RDS初始化
void dev_RDS_init(void)
{
	GPIO_InitTypeDef GPIO_InitStructure;

	// 配置GPIOA15爲RDS時鐘輸入,GPIOA8爲RDS數據輸入
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_15 | GPIO_Pin_8;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
	GPIO_Init(GPIOA, &GPIO_InitStructure);
	// 配置GPIOD2爲RDS信號質量輸入
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
	GPIO_Init(GPIOD, &GPIO_InitStructure);
	// 打開GPIOA、GPIOD時鐘
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_GPIOD,
	                       ENABLE);
}

// RDS使能
void dev_RDS_Enable(Boolean bEnable)
{
    EXTI_InitTypeDef        EXTI_InitStructure;
	NVIC_InitTypeDef        NVIC_InitStructure; 

    if(g_bRDSEnable == bEnable)
	   return;

	// 配置GPIOA15(RDS時鐘信號)上升沿中斷
	GPIO_EXTILineConfig(GPIO_PortSourceGPIOA,GPIO_PinSource15);
	EXTI_ClearITPendingBit(EXTI_Line15);
	EXTI_InitStructure.EXTI_Line = EXTI_Line15;
	EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;										  
	EXTI_InitStructure.EXTI_Trigger	= EXTI_Trigger_Rising;	
	EXTI_InitStructure.EXTI_LineCmd = bEnable ? ENABLE : DISABLE;
	EXTI_Init(&EXTI_InitStructure);
	
	// 配置中斷向量
	NVIC_ClearIRQChannelPendingBit(EXTI15_10_IRQChannel);
	NVIC_InitStructure.NVIC_IRQChannel = EXTI15_10_IRQChannel;
  	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3;
	NVIC_InitStructure.NVIC_IRQChannelCmd = bEnable ? ENABLE : DISABLE;
	NVIC_Init(&NVIC_InitStructure);		

    g_bRDSEnable = bEnable;
}

/*******************************************************************************
* Function Name  : main
* Description    : Main program.
* Input          : None
* Output         : None
* Return         : None
*******************************************************************************/
int main(void)
{
	/* Configure the system clocks */
	RCC_Configuration();
	/* NVIC Configuration */
	NVIC_Configuration();
	/* RDS initialization */
	dev_RDS_init();
	/* Enable RDS */
	dev_RDS_Enable(TRUE);
	/* RDS decode*/
	dev_RDS_decode();
}

// RDS解碼
void dev_RDS_decode(void)
{
     int32u nSynCode = 0;
	 Boolean bReSync = FALSE;	// 是否重新同步標誌
	 if(!g_bRDSExist)
	     return;
	 //dbgprt("g_bRevBitCount = %d...\n",g_bRevBitCount);
	 if(!g_bSyncFlag)  // 是否同步
	 {
		if(g_bRevBitCount<BLOCK_COUNT)
			return;
		// 計算同步校驗碼
		nSynCode = CacluSyncCode(g_block);
		//dbgprt("g_block = 0x%x,SynCode = 0x%x\n",g_block,nSynCode);
		if(nSynCode == SYNCODE_A)
		{
			g_bSyncFlag = TRUE; 
			g_blockA = g_block;
			g_nSyncLevel = 1;
			g_bRevBitCount = 0;
			dbgprt("RDS sync success!!!!!!!!!!\n");
		}
	}
	else  // RDS已同步
	{
		if(g_bRevBitCount >= BLOCK_COUNT)
		{
		    nSynCode = CacluSyncCode(g_block);
		    //dbgprt("g_block = 0x%x,SynCode = 0x%x\n",g_block,nSynCode);
			switch(nSynCode)
			{
				case SYNCODE_A:
					if(g_nSyncLevel != 0)
					{
						bReSync = TRUE;
						break;
					}
					g_blockA = g_block;
					g_nSyncLevel = 1;
					g_bRevBitCount = 0;
					dbgprt("rev BlockA[0x%04x] success...\n",g_blockA);
					break;
				case SYNCODE_B:
					if(g_nSyncLevel != 1)
					{
						bReSync = TRUE;
						break;
					}
					g_blockB = g_block;
					g_nSyncLevel = 2;
					g_bRevBitCount = 0;
					dbgprt("rev BlockB[0x%04x] success...\n",g_blockB);
					break;
				case SYNCODE_C:
				case SYNCODE_C2:
					if(g_nSyncLevel != 2)
					{
						bReSync = TRUE;
						break;
					}
					g_blockC = g_block;
					g_nSyncLevel = 3;
					g_bRevBitCount = 0;
					dbgprt("rev BlockC[0x%04x] success...\n",g_blockC);
					break;
				case SYNCODE_D:
					if(g_nSyncLevel != 3)
					{
						bReSync = TRUE;
						break;
					}
					g_blockD = g_block;
					g_nSyncLevel = 0;
					g_bGroupFlag = TRUE;
					g_bRevBitCount = 0;
					dbgprt("rev BlockD[0x%04x] success...\n",g_blockD);
					break;
				default:  // 同步校驗失敗,需重新同步
					bReSync = TRUE;
					break; 
			}  
		}
		if(bReSync)
		{
			// 重新同步
			dbgprt("ReSync RDS...\n");
			g_bSyncFlag = FALSE;
			g_nSyncLevel = 0;
		}
		if(g_bGroupFlag) // 接收到一組RDS數據,發送給89
		{
			dbgprt("rev RDS group done...\n");
			g_bGroupFlag = FALSE;
			memcpy(g_RDSGroup,&g_blockA,4);
			memcpy(g_RDSGroup+4,&g_blockB,4);
			memcpy(g_RDSGroup+8,&g_blockC,4);
			memcpy(g_RDSGroup+12,&g_blockD,4);
			// 將RDS數據發送給主CPU
			SendRdsDataToHost(g_RDSGroup,sizeof(g_RDSGroup));	
		}
	}
}

// RDS數據接收處理(RDS時鐘中斷)
void dev_RDS_RevDataHandler(void)
{
   	static int8u nGoodQualCounts = 0; 	  // 統計RDS信號質量持續有效次數
	int8u RDSDataBit = GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_8);
	// 讀取RDS信號質量信號
	if(!GPIO_ReadInputDataBit(GPIOD,GPIO_Pin_2))  // GPIOD2爲RDS信號質量輸入
	{
		 nGoodQualCounts = 0;
	     g_bRDSExist = FALSE;
	}
	else 
	{
		 nGoodQualCounts++;
	}
    // 連續N(N=10)次統計RDS信號輸出均爲高電平,認爲存在RDS信號
	if(nGoodQualCounts>10)
	{
		 g_bRDSExist = TRUE;
	     nGoodQualCounts = 0;
	} 
	//if(g_bRDSExist)
	{
	     g_block <<= 1;
		 g_block |= RDSDataBit;
		 g_block &= 0x3FFFFFF;
		 g_bRevBitCount++;
	}
}

// 計算同步校驗碼
int32u CacluSyncCode(BLOCK block)
{
	// 同步校驗矩陣H
	const int8u H[] = 
	{
		1,0,0,0,0,0,0,0,0,0,
		0,1,0,0,0,0,0,0,0,0,
		0,0,1,0,0,0,0,0,0,0,
		0,0,0,1,0,0,0,0,0,0,
		0,0,0,0,1,0,0,0,0,0,
		0,0,0,0,0,1,0,0,0,0,
		0,0,0,0,0,0,1,0,0,0,
		0,0,0,0,0,0,0,1,0,0,
		0,0,0,0,0,0,0,0,1,0,
		0,0,0,0,0,0,0,0,0,1,
		1,0,1,1,0,1,1,1,0,0,
		0,1,0,1,1,0,1,1,1,0,
		0,0,1,0,1,1,0,1,1,1,
		1,0,1,0,0,0,0,1,1,1,
		1,1,1,0,0,1,1,1,1,1,
		1,1,0,0,0,1,0,0,1,1,
		1,1,0,1,0,1,0,1,0,1,
		1,1,0,1,1,1,0,1,1,0,
		0,1,1,0,1,1,1,0,1,1,
		1,0,0,0,0,0,0,0,0,1,
		1,1,1,1,0,1,1,1,0,0,
		0,1,1,1,1,0,1,1,1,0,
        0,0,1,1,1,1,0,1,1,1,
		1,0,1,0,1,0,0,1,1,1,
		1,1,1,0,0,0,1,1,1,1,
		1,1,0,0,0,1,1,0,1,1
	};
    int8u synMatrix[10] = {0};
	int32u nSynCode = 0;
	int8u i = 0,j = 0;
    for(i=0;i<10;i++)
    {
         for(j=0;j<BLOCK_COUNT;j++)
         {
              synMatrix[i]+= ((block>>(BLOCK_COUNT-1-j))&1)*H[i+10*j];
         }
         synMatrix[i] = synMatrix[i]%2;
         nSynCode <<= 1;
         nSynCode += synMatrix[i];
    }
 return nSynCode;
}
/*******************************************************************************
* Function Name  : EXTI15_10_IRQHandler
* Description    : This function handles External lines 15 to 10 interrupt request.
* Input          : None
* Output         : None
* Return         : None
*******************************************************************************/
void EXTI15_10_IRQHandler(void)
{
	if(EXTI_GetITStatus(EXTI_Line15) != RESET)
	{
		EXTI_ClearITPendingBit(EXTI_Line15);
	    
		dev_RDS_RevDataHandler();   
	}
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章