1. 程式人生 > >NUC140之串列埠UART

NUC140之串列埠UART

UART可以說是序列通訊用的最多的協議,相對其他協議也比較簡單,一般只要設定好位元率,資料位,停止位和校驗位即可。下面來看程式。

#include "UART.h"    
#include <stdio.h>
volatile static uint8_t Rx_UART[20] = {0};          //串列埠儲存接收資料陣列,加static作為靜態全域性變數僅能在本檔案內部使用
volatile static uint8_t Rx_UART_counter = 0;        //串列埠儲存接收資料陣列計數器
volatile static uint8_t UART_one_frame_end;            //串列埠一幀資料傳輸結束標誌
volatile static uint8_t return_OK_flag = 0;            //判斷是否是設定命令標誌
volatile static uint8_t verify_data[20] = {0};        //寄存用於校驗的值的陣列
void UART_callback (uint32_t tempdata);

/**************************************************
 *函式名:    send_to_UART                           *
 *功能:    keil庫函式傳送UART                    *
 *出口引數:無                                      *
 *入口引數:無                                      *
 *************************************************/
void send_to_UART(const uint8_t *src,uint8_t len)
{
    uint8_t i = 0 ;
    printf("\n");
    for(i=0;i<len;i++)
    {
        printf("%.2X",src[i]);
    }
    printf("\r");
}

/**************************************************
 *函式名:    uint8_t check_ASCII_table             *
 *功能:    ASCII碼查表函式                       *
 *出口引數:無                                      *
 *入口引數:無                                      *
 *************************************************/
void initialize_UART_clock (void)
{
    DrvSYS_UnlockProtectedReg ();                         //解鎖受保護的系統暫存器
    DrvSYS_SetOscCtrl (E_SYS_XTL12M, 1);   
    DrvSYS_SelectIPClockSource (E_SYS_UART_CLKSRC, 0x00); //設定UART時鐘源為外部12MHz晶振
    DrvSYS_SetIPClock (E_SYS_UART0_CLK, 1);                  //使能串列埠0的時鐘
    DrvSYS_LockProtectedReg ();                           //對系統暫存器上鎖
}

/**************************************************
 *函式名:    initialize_UART                         *
 *功能:    初始化串列埠                            *
 *出口引數:無                                      *
 *入口引數:無                                      *
 *************************************************/
void initialize_UART (void)
{
    STR_UART_T sParam;
      sParam.u32BaudRate                = 9600;                      //波特率9600
    sParam.u8cDataBits                = DRVUART_DATABITS_8;          //資料位8位
    sParam.u8cStopBits                = DRVUART_STOPBITS_1;          //停止位1bit
    sParam.u8cParity                  = DRVUART_PARITY_NONE;      //無校驗
    sParam.u8cRxTriggerLevel          = DRVUART_FIFO_1BYTES;      //串列埠中斷觸發級別1個位元組
    DrvUART_Open (UART_PORT0, &sParam);                              //埠0初始化
    delay (100);                                                  //延遲使時鐘穩定

    DrvUART_EnableInt(UART_PORT0,         //使能串列埠0中斷
                        DRVUART_RDAINT,    //接收到資料進入中斷
                       UART_callback);   //中斷回撥函式
}

/**************************************************
 *函式名:    UART_callback                         *
 *功能:    串列埠接收函式                          *
 *出口引數:無                                      *
 *入口引數:有,暫無用                              *
 *************************************************/
void UART_callback (uint32_t tempdata)
{
    uint8_t temp_UART[1] = {0};                            //串列埠接收臨時儲存變數陣列
    volatile static uint8_t start_flag = 0;                //串列埠通訊開始標誌

    DrvUART_Read(UART_PORT0,                            //讀取埠0的資料
                 temp_UART,                                //儲存於該陣列中
                 1);                                    //讀取位元組數為1

    if (start_flag != 0)                                //通訊開始後才進入
    {
        if (temp_UART[0] == 13)                            //如果收到結束標誌位(ASCII碼13對應CR,結束標誌位)
        {
            UART_one_frame_end = 1;                        //表示已經接收到一幀資料,要進行判斷
            start_flag = 0;                                //清零等待下一幀的開始
        }
        else 
        {
            Rx_UART[Rx_UART_counter] = temp_UART[0];    //將接收到的位元組挨個儲存起來
            Rx_UART_counter++;                            
        }    
    }

    if (temp_UART[0] == 10)                                //如果收到起始標誌位(ASCII碼10對應LF,起始標誌位)
    {
        start_flag = 1;                                 //串列埠通訊開始        
    }
    if (UART_one_frame_end == 1)                                             //串列埠接收到了命令
    {
        if (UART_Rx_verify())                                                 //如果校驗結果一致
        {
            UART_Rx_solve();                                                 //接收資料處理函式
              if(return_OK_flag == 1)
            {
                UART_Tx(RX_OK_COMMMAND);                                       //回傳收到的資料與檢驗結果一致(以‘B’開頭的設定命令要返回,以'A'開頭的查詢命令無需返回) 
                return_OK_flag = 0;
            }
        }
        else
        {
            UART_Tx(RX_ERROR_COMMAND);                                       //回傳收到的資料與檢驗結果不一致 
        }
    }
}

/**************************************************
*函式名:  UART_Rx_verify                          *
*功能:       串列埠接收資料校驗函式                   *
*出口引數:為1:接收資料與校驗結果一致              *
           為0:接收資料與校驗結果不一致          *
*入口引數:無                                      *
***************************************************/
uint8_t UART_Rx_verify (void) 
{
    uint8_t verify_value = 0;                           
    uint8_t verify_value_Rx = 0;                        
    uint8_t i;
    uint8_t temp_data[20] = {0};

    check_ASCII_table((uint8_t *)Rx_UART, (uint8_t *)temp_data, Rx_UART_counter); //收到校驗碼後先將ASCII碼轉譯為16進位制數值後再校驗
    combine((uint8_t *)temp_data, (uint8_t *)verify_data, Rx_UART_counter);           //轉譯後合併
    for (i=0; i<(Rx_UART_counter/2-1); i++)                //和校驗,溢位不管,由於收到的最後兩個資料是校驗值,所以不計入該次計算中
    {
        verify_value *= 3;
        verify_value += verify_data[i];      
    }
    verify_value_Rx = verify_data[Rx_UART_counter/2-1]; //讀取傳送回來的校驗值

    if ((verify_value == verify_value_Rx)&&((Rx_UART_counter%2)==0))     //收到的一定是偶數個位元組
    {
        Rx_UART_counter = 0;                            //計數器清零
        return (1);                                        //返回接收資料與校驗結果一致,開始判斷
    }
    else
    {
        Rx_UART_counter = 0;                            //計數器清零
        UART_one_frame_end = 0;                         //由於不必判斷,清除一幀接收完成標誌,可以接收下一幀資料
        return (0);                                        //返回接收資料與校驗結果不一致,不必判斷
    }
}
/**************************************************
 *函式名:    UART_Rx_solve                          *
 *功能:    串列埠接收資料處理函式                  *
 *出口引數:無                                      *
 *入口引數:無                                      *
 *************************************************/
void UART_Rx_solve(void)
{
    if (Rx_UART[0] == 'B')
    {                                                                       
        return_OK_flag = 1;    
    }
    else
    {
        return_OK_flag = 0;    
    }

    if (verify_data[0] == 0xb1)                                               
    {
        power_value = CHECK_BCD_TABLE(verify_data[2]);                          
        if (power_value > 50)                                                  
        {                                                         
            power_value = 50;    
        }
        else if (power_value < 10)
        {
            power_value = 10;
        }
        SPI_U28_write(power_setup_table[power_value]);                        
        at24c32_write(DEVICE_ADDRESS, POWER_VALUE_HIGH_ADDRESS, POWER_VALUE_LOW_ADDRESS, power_value);
        SPI_U27_write(modulation_setup_table[power_value][modulation_value]); 
    }
    
    else if (verify_data[0] == 0xb2)                                       
    {
        modulation_value = CHECK_BCD_TABLE(verify_data[2]);                 
        if (modulation_value > 99)                                             
        {
            modulation_value = 99;                       
        }
        else if (modulation_value < 30)                                  
        {
            modulation_value = 30;                       
        }
        SPI_U27_write(modulation_setup_table[power_value][modulation_value]);
        at24c32_write(DEVICE_ADDRESS, MODULATION_VALUE_HIGH_ADDRESS, MODULATION_VALUE_LOW_ADDRESS, modulation_value);
    }
                                  
    else if (verify_data[0] == 0xb3)                                  
    {
        if (verify_data[2] == 1)                                  
        {
            radio_main = 1;                                         
        }
        else
        {
            radio_main = 0;                                                
        }
        at24c32_write(DEVICE_ADDRESS, RADIO_MAIN_HIGH_ADDRESS, RADIO_MAIN_LOW_ADDRESS, radio_main);
    }
    
    else if (verify_data[0] == 0xb4)                        
    {
        if (verify_data[2] == 0)                                     
        {
            enable_PTT_limit = 0;                                         
            at24c32_write(DEVICE_ADDRESS, ENABLE_PTT_LIMIT_HIGH_ADDRESS, ENABLE_PTT_LIMIT_LOW_ADDRESS, 0); //存入新的是否使能發定時功能預設值
        }
        else
        {
            enable_PTT_limit = 1;                                         
            at24c32_write(DEVICE_ADDRESS, ENABLE_PTT_LIMIT_HIGH_ADDRESS, ENABLE_PTT_LIMIT_LOW_ADDRESS, 1); //存入新的是否使能發定時功能預設值
            PTT_limit_time = verify_data[2] * 60;                             //發定時時間資料是按檔位發過來的
            if (PTT_limit_time > 300)                                      
            {
                PTT_limit_time = 300;
            }
            DrvTIMER_ClearTimerEvent(E_TMR0,0);                                 
            setup_ptt_limit_time (PTT_limit_time);                         
            at24c32_write(DEVICE_ADDRESS, PTT_LIMIT_TIME_HIGH_ADDRESS, PTT_LIMIT_TIME_LOW_ADDRESS, (Rx_UART[2] - 48)); //存入新的發定時時間預設值
        }
    }
    
    else if (verify_data[0] == 0xb5)                                  
    {
        if (verify_data[2] == 0)                                           
        {
            enable_error = 0;                                             
            at24c32_write(DEVICE_ADDRESS, ENABLE_ERROR_HIGH_ADDRESS, ENABLE_ERROR_LOW_ADDRESS, 0); //存入新的是否使能報錯關二次預設值
        }
        else
        {
            enable_error = 1;                                               
            at24c32_write(DEVICE_ADDRESS, ENABLE_ERROR_HIGH_ADDRESS, ENABLE_ERROR_LOW_ADDRESS, 1); //存入新的是否使能報錯關二次預設值                
        }
    }

    else if (verify_data[0] == 0xb6)                                      
    {
    
        carrier_frequency = CHECK_BCD_TABLE(verify_data[2]) * 10000 + CHECK_BCD_TABLE(verify_data[3]) * 100 + CHECK_BCD_TABLE(verify_data[4]);/

        if (carrier_frequency < 118000)                                        
        {
            carrier_frequency = 118000;
        }
        if (carrier_frequency > 144000)                                         
        {
            carrier_frequency = 144000;
        }
        if ((carrier_frequency%25)==0)                                          
        {
            channel_space = 1;
            DrvGPIO_ClrBit (E_GPC, 5);                                        //PC.5置0
        }
        else                                                                 
        {
            channel_space = 0;    
            DrvGPIO_SetBit (E_GPC, 5);                                        //PC.5置1
        }

        carrier_frequency /= 1000;
        at24c32_write(DEVICE_ADDRESS, CHANNEL_SPACE_HIGH_ADDRESS, CHANNEL_SPACE_LOW_ADDRESS, channel_space);//存入通道間隔新值
        at24c32_write(DEVICE_ADDRESS, FREQUENCY_HIGH_ADDRESS, FREQUENCY_LOW_ADDRESS, carrier_frequency);//存入電臺地址新值
    }
    
    else if (verify_data[0] == 0xb7)                                    
    {
        if (verify_data[2] == 0)                                              
        {
            Rx_SQ(0);                                                 
        }
        else
        {
            Rx_SQ(1);                                                     
        }
    }

    else if (verify_data[0] == 0xb8)                                   
    {
        if (verify_data[2] == 0)                                           
        {
            status_register2.status_sample.PC_off = 1; //置位狀態標誌                                                     
    
        }
        else
        {
            status_register2.status_sample.PC_off = 0; //置位狀態標誌    
        }
    }

    else if (verify_data[0] == 0xb9)                                        
    {
        error_register1.error_sample_all = 0;                                 
        temperature_error_counter = 0;                                          
        supply_28V_error_counter = 0;                                     
        DISF_solve_counter = 0;                                               
        DIRR_solve_counter = 0;                                             

    else if (verify_data[0] == 0xba)                                  
    {
        if (verify_data[2] == 0)                                            
        {
            PTT_enable = 0; //禁發PTT                                                     
    
        }
        else
        {
            PTT_enable = 1; //允許發PTT    
        } 
    }    
            
    else if (verify_data[0] == 0xa1)                                      
    {
        UART_Tx (DISF_DISR_DISM_VALUE_COMMAND);    
    }
    
    else if (verify_data[0] == 0xa2)                                          
    {
        UART_Tx (READ_ERROR_COMMAND);    
    }
    
    else if (verify_data[0] == 0xa3)                                         
    {
        UART_Tx (READ_TEMPERATURE_COMMAND);    
    }

    else if (verify_data[0] == 0xa6)                                 
    {
        UART_Tx (RECEIVER_AGC_COMMMAND);    
    }
    
    else if (verify_data[0] == 0xa7)                                      
    {
        UART_Tx (READ_ALL_STATUS_COMMMAND);    
    }
        
    UART_one_frame_end = 0;                                                        //清除一幀接收完成標誌,可以接收下一幀資料
} 

/**************************************************
 *函式名:    UART_Tx                                *
 *功能:    串列埠傳送函式                          *
 *出口引數:無                                      *
 *入口引數:傳送命令型別                          *
 *************************************************/
void UART_Tx (uint8_t command)
{
    uint8_t temp_UART[1] = {0};               //串列埠傳送臨時儲存變數陣列
    uint8_t Tx_UART[20] = {0};               //串列埠傳送儲存變數陣列
    uint8_t temp_data[20] = {0};
    uint8_t temp_data_verify[20] = {0};
    uint8_t Tx_UART_counter = 0;           //串列埠傳送儲存變數陣列計數器
    uint8_t verify_value = 0;              //校驗值
    uint8_t verify_value_first;            //校驗值第一位
    uint8_t verify_value_second;           //校驗值第二位

    uint8_t i;
    uint8_t j;

    Tx_UART[0] = 10;                               //無論哪一條命令一定先發起始標誌'LF' ASCII對應10
    Tx_UART[1] = 'A';                              //無論哪一條命令命令格式第一個都是字元'A'
                                                   //這兩句放前面可以減少過多的重複程式碼
    if (command == DISF_DISR_DISM_VALUE_COMMAND)     
    {
        Tx_UART[2] = '1';                               
        Tx_UART[3] = '0';                               //資料長度為3, 分兩個位元組表示
        Tx_UART[4] = '3';                            
        Tx_UART[5] = DISF_value / 10 + '0';               //加上'0',即轉換成字元型,看ASCII碼錶就明白了(十位不可能超過9,沒有大於160的資料)
        Tx_UART[6] = DISF_value % 10 + '0';             

        Tx_UART[7] = DISR_value / 10 + '0';        
        Tx_UART[8] = DISR_value % 10 + '0';            

        Tx_UART[9] = DISM_value / 10 + '0';            
        Tx_UART[10] = DISM_value % 10 + '0';          
        Tx_UART_counter = 10;
    }

    else if (command == READ_ERROR_COMMAND)                          //如果是要讀取總的錯誤訊號
    {
        Tx_UART[2] = '2';                                          //傳送總的錯誤訊號
        Tx_UART[3] = '0';                                          //資料長度為2
        Tx_UART[4] = '2';                                                      
        Tx_UART[5] = (error_register1.error_sample_all & 0xf0)>>4;  //總的錯誤訊號暫存器1高4位
        NUMBER_TO_BCD(Tx_UART[5]);
        Tx_UART[6] = error_register1.error_sample_all & 0x0f;       //總的錯誤訊號暫存器1低4位
        NUMBER_TO_BCD(Tx_UART[6]);
        Tx_UART[7] = (status_register2.status_sample_all & 0xf0)>>4;//總的錯誤訊號暫存器2高4位
        NUMBER_TO_BCD(Tx_UART[7]);
        Tx_UART[8] = status_register2.status_sample_all & 0x0f;     //總的錯誤訊號暫存器2低4位
        NUMBER_TO_BCD(Tx_UART[8]);
        Tx_UART_counter = 8;
    }

    else if (command == READ_TEMPERATURE_COMMAND)       //如果是要讀取溫度的命令
    {
        Tx_UART[2] = '3';                               //傳送溫度值
        Tx_UART[3] = '0';                               //資料長度為1    
        Tx_UART[4] = '1';                                
        Tx_UART[5] = temperature_value / 10 + '0';       //取溫度值的十位值
        Tx_UART[6] = temperature_value % 10 + '0';       //取溫度值的個位值
        Tx_UART_counter = 6;    
    }

    else if (command == RX_ERROR_COMMAND)      //如果是要傳送接收到的資料與校驗碼不一致
    {
        Tx_UART[2] = '4';                      
        Tx_UART[3] = '0';                       //資料長度為0
        Tx_UART[4] = '0';
        Tx_UART_counter = 4;
    }

    else if (command == RX_OK_COMMMAND)           //如果是要傳送接收到的資料與校驗碼一致
    {
        Tx_UART[2] = '5';
        Tx_UART[3] = '0';                       //資料長度為0
        Tx_UART[4] = '0';
        Tx_UART_counter = 4;                  
    }

    else if (command == RECEIVER_AGC_COMMMAND)
    {
        Tx_UART[2] = '6';
        Tx_UART[3] = '0';                       //資料長度為1    
        Tx_UART[4] = '1';                                        
        Tx_UART[5] = '0';
        Tx_UART[6] = Rx_AGC() + '0';
        Tx_UART_counter = 6;
    }

    else if (command == READ_ALL_STATUS_COMMMAND) 
    {
        Tx_UART[2] = '7';
        Tx_UART[3] = '0';                             //資料長度為
        Tx_UART[4] = '4';

        Tx_UART[5] = (error_register1.error_sample_all & 0xf0)>>4;  
        NUMBER_TO_BCD(Tx_UART[5]);
        Tx_UART[6] = error_register1.error_sample_all & 0x0f;       
        NUMBER_TO_BCD(Tx_UART[6]);
        Tx_UART[7] = (status_register2.status_sample_all & 0xf0)>>4;
        NUMBER_TO_BCD(Tx_UART[7]);
        Tx_UART[8] = status_register2.status_sample_all & 0x0f;     
        NUMBER_TO_BCD(Tx_UART[8]);

        Tx_UART[9] = temperature_value / 10 + '0';      
        Tx_UART[10] = temperature_value % 10 + '0';                                      
        
        Tx_UART[11] = '0';
        Tx_UART[12] = Rx_AGC() + '0';
        
        Tx_UART_counter = 12;
    }

    check_ASCII_table((uint8_t *)(Tx_UART+1), (uint8_t *)temp_data, Tx_UART_counter); //收到校驗碼後先將ASCII碼轉譯為16進位制數值後再校驗
    combine((uint8_t *)temp_data, (uint8_t *)temp_data_verify, Tx_UART_counter);     
    for (i=0; i<(Tx_UART_counter/2); i++)     //計算校驗值,溢位不管,注意條件是<=
    {
        verify_value *= 3;
        verify_value += temp_data_verify[i]; //第0個元素是起始標誌,不校驗
    }
    verify_value_first = verify_value / 16;     //取校驗值的十位數,按16進位制
       verify_value_second = verify_value % 16; //取校驗值的個位數,按16進位制
    NUMBER_TO_BCD(verify_value_first);
    NUMBER_TO_BCD(verify_value_second);

    Tx_UART_counter += 2;             //加2準備儲存校驗值,校驗值佔2位
    Tx_UART[Tx_UART_counter - 1] = verify_value_first; //儲存校驗值第一位(十位)
    Tx_UART[Tx_UART_counter] = verify_value_second;    //儲存校驗值第二位(個位)
    Tx_UART_counter += 1;             //加1準備儲存結束標誌
    Tx_UART[Tx_UART_counter] = 13;                     //儲存結束標誌'CR'對應ASCII碼13

    for (j=0; j<=Tx_UART_counter; j++)
    {
        temp_UART[0] = Tx_UART[j];
        DrvUART_Write(UART_PORT0,             //UART埠0
                        (uint8_t *)temp_UART,     //要傳送的資料儲存陣列地址
                        1);                     //傳送位元組數
    }
}

標頭檔案如下

#ifndef UART_h
#define UART_h

#include "stdint.h"
#include "DrvUART.h"
#include "SPI_U27_U28.h"
#include "I2C_remote.h"    
#include "at24c32.h"
#include "timer.h"
#include "delay.h"
#include "common_functions.h"
#include "I2C_remote.h"

#define DISF_DISR_DISM_VALUE_COMMAND 0x01  
#define READ_ERROR_COMMAND           0x02  //讀取總的錯誤訊號命令
#define READ_TEMPERATURE_COMMAND     0x03  //讀取溫度狀態命令
#define RX_ERROR_COMMAND             0x04  //串列埠接收資料與校驗值不一致
#define RX_OK_COMMMAND               0x05  //串列埠接收資料與校驗值一致
#define RECEIVER_AGC_COMMMAND        0x06 
#define READ_ALL_STATUS_COMMMAND     0x07  

extern void initialize_UART_clock (void) ; //初始化串列埠時鐘
extern void initialize_UART (void);        //初始化UART
extern void UART_Tx (uint8_t command);     //給前面板傳送狀態,引數對應傳送的狀態型別
extern uint8_t UART_Rx_verify (void);      //串列埠接收到資料校驗函式
extern void UART_Rx_solve(void);           //串列埠接收資料處理函式
#endif