【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進行操作。