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