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

发布了15 篇原创文章 · 获赞 9 · 访问量 5843

猜你喜欢

转载自blog.csdn.net/zhandouhu/article/details/104092238