【EtherCAT實踐篇】三、EtherCAT從站軟件設計-IO口操作

      【EtherCAT分析】二、EtherCAT從站驅動程序分析已經給出了EtherCAT從站軟件設計的基本框架,下面結合設計的EtherCAT從站硬件板子進行如程序設計。

1、STM32底層引腳及功能配置

主要完成RCC時鐘,GPIO口、AD採樣、SPI接口等配置。

1.1 GPIO口配置:16路撥碼開關輸入,16路LED輸出

void GPIO_init(void)
{
   GPIO_InitTypeDef GPIO_InitStructure;
   
    //OUTPUT:LED0-15
   GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_2 | GPIO_Pin_3| GPIO_Pin_4| GPIO_Pin_5| GPIO_Pin_6| GPIO_Pin_7;
   GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;  //PB13/14/15複用推輓輸出 
   GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
   GPIO_Init(GPIOE, &GPIO_InitStructure);//初始化GPIOE
    
   GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8 | GPIO_Pin_9 | GPIO_Pin_10 | GPIO_Pin_11| GPIO_Pin_12| GPIO_Pin_13| GPIO_Pin_14| GPIO_Pin_15;
   GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;  //PB13/14/15複用推輓輸出 
   GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
   GPIO_Init(GPIOE, &GPIO_InitStructure);//初始化GPIOE

     //INPUT:INPUT0-7
     GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8 | GPIO_Pin_9 | GPIO_Pin_10 | GPIO_Pin_11| GPIO_Pin_12| GPIO_Pin_13| GPIO_Pin_14| GPIO_Pin_15;
   GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPD;  //PB13/14/15複用推輓輸出 
   GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
   GPIO_Init(GPIOB, &GPIO_InitStructure);//初始化GPIOB
     //INPUT:INPUT8-15
   GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_2 | GPIO_Pin_3| GPIO_Pin_4| GPIO_Pin_5| GPIO_Pin_6| GPIO_Pin_7;
   GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPD;  //PB13/14/15複用推輓輸出 
   GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
   GPIO_Init(GPIOC, &GPIO_InitStructure);//初始化GPIOC
     
   GPIO_Write(GPIOE,0);//默認LED都滅
}

1.2 ADC採樣配置:1路模擬輸入

void  Adc_Init(void)
{     
    ADC_InitTypeDef ADC_InitStructure; 
    GPIO_InitTypeDef GPIO_InitStructure;

    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB |RCC_APB2Periph_ADC1    , ENABLE );      //使能ADC1通道時鐘

    RCC_ADCCLKConfig(RCC_PCLK2_Div6);   //設置ADC分頻因子6 72M/6=12,ADC最大時間不能超過14M

    //PB1 作爲模擬通道輸入引腳                         
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;        //模擬輸入引腳
    GPIO_Init(GPIOB, &GPIO_InitStructure);    

    ADC_DeInit(ADC1);  //復位ADC1,將外設 ADC1 的全部寄存器重設爲缺省值   

   ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;    //ADC工作模式:ADC1和ADC2工作在獨立模式
    ADC_InitStructure.ADC_ScanConvMode = DISABLE;    //模數轉換工作在單通道模式
    ADC_InitStructure.ADC_ContinuousConvMode = DISABLE;    //模數轉換工作在單次轉換模式
    ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;    //轉換由軟件而不是外部觸發啓動
    ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;    //ADC數據右對齊
    ADC_InitStructure.ADC_NbrOfChannel = 1;    //順序進行規則轉換的ADC通道的數目
    ADC_Init(ADC1, &ADC_InitStructure);    //根據ADC_InitStruct中指定的參數初始化外設ADCx的寄存器   
    ADC_Cmd(ADC1, ENABLE);    //使能指定的ADC1
    ADC_ResetCalibration(ADC1);    //使能復位校準  
    while(ADC_GetResetCalibrationStatus(ADC1));    //等待復位校準結束
    ADC_StartCalibration(ADC1);     //開啓AD校準
    while(ADC_GetCalibrationStatus(ADC1));     //等待校準結束

}        

1.3 SPI配置

void SPI1_Init(void)
{
     GPIO_InitTypeDef GPIO_InitStructure;
      SPI_InitTypeDef  SPI_InitStructure;

    RCC_APB2PeriphClockCmd(    RCC_APB2Periph_GPIOA, ENABLE );//
    RCC_APB2PeriphClockCmd(    RCC_APB2Periph_SPI1,  ENABLE );//
 
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5 | GPIO_Pin_6 | GPIO_Pin_7;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;  
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOA, &GPIO_InitStructure);//

    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;  //
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOA, &GPIO_InitStructure);//

    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2|GPIO_Pin_3;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;  
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOA, &GPIO_InitStructure);//

     GPIO_SetBits(GPIOA,GPIO_Pin_5|GPIO_Pin_6|GPIO_Pin_7);  
    GPIO_SetBits(GPIOA, GPIO_Pin_4);

    SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex;  //設置SPI單向或者雙向的數據模式:SPI設置爲雙線雙向全雙工
    SPI_InitStructure.SPI_Mode = SPI_Mode_Master;        //設置SPI工作模式:設置爲主SPI
    SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b;        //設置SPI的數據大小:SPI發送接收8位幀結構
    SPI_InitStructure.SPI_CPOL = SPI_CPOL_High;        //串行同步時鐘的空閒狀態爲高電平
    SPI_InitStructure.SPI_CPHA = SPI_CPHA_2Edge;    //串行同步時鐘的第二個跳變沿(上升或下降)數據被採樣
    SPI_InitStructure.SPI_NSS = SPI_NSS_Soft;        //NSS信號由硬件(NSS管腳)還是軟件(使用SSI位)管理:內部NSS信號有SSI位控制
    SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_256;        //定義波特率預分頻的值:波特率預分頻值爲256
    SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;    //指定數據傳輸從MSB位還是LSB位開始:數據傳輸從MSB位開始
    SPI_InitStructure.SPI_CRCPolynomial = 7;    //CRC值計算的多項式
    SPI_Init(SPI1, &SPI_InitStructure);  //根據SPI_InitStruct中指定的參數初始化外設SPIx寄存器
//     SPI_I2S_ITConfig(SPI1,SPI_I2S_IT_RXNE,ENABLE);
    SPI_Cmd(SPI1, ENABLE); //使能SPI外設  

1.4 SPI通訊程序:包括8位、16位、32位數據讀取和寫入,這裏列出8位數據通訊程序:

u8 spi_read_8(u16 address)
{
     u8 high;
     u8 low;
     u8 result;
     u16 temp;
     temp=(address<<3)|(0x02);
     high=(u8)(temp>>8);
     low=(u8)temp;
     GPIO_ResetBits(GPIOA, GPIO_Pin_4);
     SPI1_ReadWriteByte(high);
     SPI1_ReadWriteByte(low);
     result=SPI1_ReadWriteByte(0xFF);
     GPIO_SetBits(GPIOA, GPIO_Pin_4);
     return result;
}

void spi_write_8(u16 address,u8 data)
{
     u8 high;
     u8 low;
     u8 result;
     u16 temp;
     temp=(address<<3)|(0x04);
     high=(u8)(temp>>8);
     low=(u8)temp;
     GPIO_ResetBits(GPIOA, GPIO_Pin_4);
     SPI1_ReadWriteByte(high);
     SPI1_ReadWriteByte(low);
     SPI1_ReadWriteByte(data);
     GPIO_SetBits(GPIOA, GPIO_Pin_4);
}

2、ESC驅動程序

2.1 從站驅動程序頭文件ec_def.h

    從站驅動程序頭文件主要定義了重要的常章、數據結構及全局變章。這裏參考《工業以太網現場總線EtherCAT驅動程序設計及應用》書籍進行設計。這裏僅列出部分

//協議相關變量定義,主要爲ProcessData和MailBox
#define    MAX_RX_PDOS 0x0001
#define MAX_TX_PDOS 0x0001
#define MIN_PD_WRITE_ADDRESS 0x1000
#define MAX_PD_WRITE_ADDRESS 0x2000
#define MIN_PD_READ_ADDRESS 0x1000
#define MAX_PD_READ_ADDRESS 0x2000
#define NO_OF_PD_INPUT_BUFFER 0x0003
#define NO_OF_PD_OUTPUT_BUFFER 0x0003

#define MAX_PD_INPUT_SIZE 0x0040
#define MAX_PD_OUTPUT_SIZE 0x0040
#define MAX_MB_INPUT_SIZE 0x0040
#define MAX_MB_OUTPUT_SIZE 0x0040
#define MIN_MBX_SIZE 0x0020
#define MAX_MBX_SIZE 0x0400
#define MIN_MBX_WRITE_ADDRESS 0x1000
#define MIN_MBX_READ_ADDRESS 0x1000
#define MAX_MBX_WRUTE_ADDRESS 0x2000
#define MAX_MBX_READ_ADDRESS 0x2000

2.2  ESC基本操作函數

對ESC的操作,主要是根據ESC各寄存器的地址,然後進行相應讀寫操作。下面列出其中幾個重要的操作函數:

(1)設置應用層狀態:

void SetAlStatus(u16 alstatus,u16 alstatuscode)
{
    spi_write_16(0x0130,alstatus);
    if(alstatuscode!=0xFF)
    spi_write_16(0x0134,alstatuscode); 
}

(2)設置事件中斷屏蔽寄存器

void set_intmask(u16 intMask)
{
    u16 mask;
    mask= spi_read_16(0x0204);
    mask=mask|intMask;
    spi_write_16(0x0204,mask);
}

void reset_intmask(u16 intMask)//復位中斷屏蔽寄存器
{
    u16 mask;
    mask= spi_read_16(0x0204);
    mask=mask&intMask;
    spi_write_16(0x0204,mask);
}

(3)SM通道操作程序:

void enable_syncmanchannel(u8 channel)//使能SM運行
{
    u16    address;
    u8 temp;
    address=0x0800+channel*0x08;
    temp=spi_read_8(address+7);
    temp &=~((u8)SM_PDIDISABLE);
    spi_write_8(address+7,temp);
}

void disable_syncmanchannel(u8 channel)//禁止SM運行
{
    u16    address;
    u8 temp;
    address=0x0800+channel*0x08;
    temp=spi_read_8(address+7);
    temp |=((u8)SM_PDIDISABLE);
    spi_write_8(address+7,temp);
}

TSYNCMAN get_sm(u8 channel)//獲取SyncManager寄存器通道號
{
     TSYNCMAN temp;
     u16 address;
     address=0x0800+channel*0x08;
     temp.sm_physical_addr=spi_read_16(address);
     temp.sm_length=spi_read_16(address+2);
     temp.sm_register_control=spi_read_8(address+4);
     temp.sm_register_status=spi_read_8(address+5);
     temp.sm_register_activate=spi_read_8(address+6);
     temp.sm_register_pdictl=spi_read_8(address+7);
     return temp;
}

3、應用層IO操作函數

(1)main函數

int main()
{
//    int i=0;
    HW_init();
    ECAT_init();                          
   
while(1)
    {
        //讀應用事件請求寄存器
        EscAlEvent=spi_read_32(0x0220);
        if(!bEscIntEnabled)
        {
             //未使能中斷,處於自由運行模式
            free_run();        //查看週期性數據
        }
        al_event();      //應用層事件處 理,包括狀態機和非週期通訊
            }

    return 0;
}

(2)IO數據讀取函數

void readoutputdata(void)
{
    u16 i;
    u16 address;
    u16 tmp;

    for(i=0,address=nEscAddrOutputData;i<nPdOutputSize;i++,address++)
    {
        aPdOutputData[i]= spi_read_8(address);
    }
    tmp=aPdOutputData[0]+ (aPdOutputData[1]<<8);//接收主站發來的數據
    GPIO_Write(GPIOE,tmp);//通過LED上進行顯示
    
}

(3)數據IO寫入函數

void writeinputdata(void)
{
   u16 i;
   u16 address;
   
   for(i=0,address=nEscAddrInputData;i<nPdInputSize;i++,address++)
     {
       if(i==0)spi_write_8(address,GPIO_ReadInputData(GPIOB)>>8);
         else if(i==1)spi_write_8(address,(GPIO_ReadInputData(GPIOC)%256));
         else if(i==2)spi_write_8(address,ADvalue/256);//模擬量分爲高低位發送
         else if(i==2)spi_write_8(address,ADvalue%256);
         else spi_write_8(address,0xAA);     
     }
}

因篇幅有限,僅列出部分程序,後面利用TwinCAT3 對IO進行操作。

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