0x00前言

因为前文有说到对于HC32F460的相关引脚测试,笔者最近完成了一个按照器件化简化的SPI的总线,所以放在MCU上测试一下效果。

0x10 介绍

作为一个SPI模拟器,他至少需要:

  • 一个设置的方式
  • 一个查看当前状态的接口
  • 一个写入接口
  • 一个读取接口

前文介绍过SPI的特性,这里不再赘述:

typedef union 
{
	unsigned char d8;
	struct
	{
	unsigned CPHA:1;		//0-even-catch|1-odd-catch
	unsigned CPOL:1;		//0-low|1-high
	unsigned DATASIZE:1;	//0-8bit|1-16bit
	unsigned FLASH_BIT:1;	//0-MSB|1-LSB
	unsigned MODE:1;		//0-master|1-slave
	unsigned DIR:2;		//0-Full duplex|1-onlyRX|2-onlyTX|3-Only_listen
	unsigned ENABLE :1; //0-off|1-on
	}b;
	
}S_SPI_CONFIG;
typedef struct
{
	unsigned CLK:1;
	unsigned CS:1;		
	unsigned MOSI:1;	
	unsigned MISO:1;	
	unsigned NU :28; 
}S_SPI_SOFT_PIN;

这就是当前的虚拟设备的模式与引脚建模,这里只实现了Master、Mode 0模式,剩下的模式没有测试

这里HC32F460库没有对于当前GPIO单个驱动的接口,所以笔者自己做了一个

void Port_Write_Pins(en_port_t enPort, uint16_t u16Pin,unsigned char state)
{
    uint16_t *PORx;
    if(state)
    {
        PORx = (uint16_t *)((uint32_t)(&M4_PORT->POSRA) + 0x10u * enPort);
    }
    else
    {
        PORx = (uint16_t *)((uint32_t)(&M4_PORT->PORRA) + 0x10u * enPort);
    }
    *PORx |= u16Pin;
}

下面还需要一个基本的实现,也就是使用一个定时器的模型进行模拟当前的SPI设备:

void TIM_IRQHandler()
    {
        {
            if(spi_soft_obj.config.b.ENABLE == 1)
            {
                //normal-no send
                if(spi_soft_obj.line_state == 0)
                {
                    if(spi_soft_obj.config.b.CPOL)
                    {
                        spi_pin.CLK = 1;
                    }
                    else
                    {
                        spi_pin.CLK = 0;
                    }
                    //spi_pin.MOSI = 0;
                    if(spi_pin.CS == 1)
                    {
                        //MSB
                        if(spi_soft_obj.config.b.FLASH_BIT == 0)
                        {
                            spi_soft_obj.data[SOFT_SPI_DATA_IN] |= MISO_GET();
                        }
                        //LSB
                        else
                        {   //no debug
                            spi_soft_obj.data[SOFT_SPI_DATA_IN] |= MISO_GET()<<((8*(spi_soft_obj.config.b.DATASIZE+1)));
                        }
                    }
                    spi_pin.CS = 0;
                    update_bit =0;
                    clock_count = 0;
                    spi_bit_count = 0;
                }
                //send-once byte
                else
                {
                        if((clock_count == 0))
                        {
                            spi_soft_obj.data[SOFT_SPI_DATA_IN] = 0;
                        }
                    //0-CPHA=0 
                    if((!spi_soft_obj.config.b.CPHA) && (clock_count == 0))
                    {
                        update_bit = 1;
                        spi_pin.CLK = 0;
                    }
                    else
                    {
                        spi_pin.CLK = !spi_pin.CLK;
                        if((clock_count % 2) == (spi_soft_obj.config.b.CPHA))
                        {
                            update_bit = 1;
                        }
                        else
                            update_bit = 0;
                    }
                    clock_count++;
                    //send_finish
                    if((8*(spi_soft_obj.config.b.DATASIZE+1)) <= spi_bit_count)
                    {
    
                        spi_soft_obj.line_state = 0;
                        //spi_pin.MOSI = 0;
                        spi_pin.CS = 1;
                        update_bit =0;
                        clock_count = 0;
                        spi_bit_count = 0;
                    }
                    if(update_bit)
                    {
                        spi_pin.MISO = MISO_GET();
                        //MSB
                        if(spi_soft_obj.config.b.FLASH_BIT == 0)
                        {
                            spi_pin.MOSI = spi_soft_obj.data[SOFT_SPI_DATA_OUT] >> ((8*(spi_soft_obj.config.b.DATASIZE+1)) - spi_bit_count - 1 ) & 0x01;
                            spi_soft_obj.data[SOFT_SPI_DATA_IN] |= MISO_GET()<<((8*(spi_soft_obj.config.b.DATASIZE+1))- spi_bit_count );
                        }
                        //LSB
                        else
                        {   //no debug
                            spi_pin.MOSI = spi_soft_obj.data[SOFT_SPI_DATA_OUT] >> (spi_bit_count) & 0x01;
                            spi_soft_obj.data[SOFT_SPI_DATA_IN] |= MISO_GET()<<(spi_bit_count);
                        }
                        spi_bit_count++;
                    }
                    
                    
                }
                CLK_SET(spi_pin.CLK);
                MOSI_SET(spi_pin.MOSI);
                
            }
            else
            {
                CLK_SET(0);
                MOSI_SET(0);
            }
        }
    }

这里就已经得到了一个基本的器件模拟化的代码。只要接入定时器的接口就可以直接实现SPI的通信了。这里笔者选择了PB 6 7 8 9作为IO的端口

void spi_soft_init()
    {
        stc_port_init_t port_init_struct;
        port_init_struct.enPinMode = Pin_Mode_Out;
        port_init_struct.enPullUp = Enable;
        port_init_struct.enLatch = Disable;
        port_init_struct.enExInt = Disable;
        port_init_struct.enInvert = Disable;
        port_init_struct.enPullUp = Enable;
        port_init_struct.enPinDrv = Pin_Drv_H;
        port_init_struct.enPinOType = Pin_OType_Cmos;
        port_init_struct.enPinSubFunc = Enable;
        
        
        PORT_Init(PortB, Pin06, &port_init_struct);
        PORT_Init(PortB, Pin07, &port_init_struct);
        PORT_Init(PortB, Pin08, &port_init_struct);
        port_init_struct.enPinMode = Pin_Mode_In;
        port_init_struct.enPinOType = Pin_OType_Od;
        port_init_struct.enPullUp = Disable;
        PORT_Init(PortB, Pin09, &port_init_struct);
        //PORT_SetFunc(PortA,Pin05, Func_Tima0, Enable);
    
        //TIM -- 模拟当前的频率

    
        spi_soft_obj.config.b.ENABLE = 1;
    
        

    timer_id = timer_add(
                        0,
                        0,
                        0,
                        0,
                        0,
                        1,
                        0,
                        1,
                        0,
                        TIM_IRQHandler
                        );
        
    }

现在还不知道到底哪个端口做什么工作。于是笔者在这里使用宏定义进行约束,也方便后期维护,这里只是示例,也可以进行更精确的GPIO的宏定义替换。

#define CS_HIGH do{PORT_SetBits(PortB, Pin06);}while(0)
#define CS_LOW do{PORT_ResetBits(PortB, Pin06);}while(0)

#define CLK_SET(state) do{Port_Write_Pins(PortB, Pin07, state);}while(0)


#define MOSI_SET(state) do{Port_Write_Pins(PortB, Pin08, state);}while(0)
#define MISO_GET() (PORT_GetBit(PortB,Pin09))

最后,就是外部需要的接口了:

char SPI_GetBusBusy()
{
	while(spi_pin.CS || spi_soft_obj.line_state);
	return spi_pin.CS && spi_soft_obj.line_state;
}

void SPI_SendData(unsigned short data)
{
	if((spi_soft_obj.config.b.ENABLE) && (!spi_soft_obj.config.b.MODE))
		spi_soft_obj.data[SOFT_SPI_DATA_OUT] = data;
	spi_soft_obj.line_state = 1;
}

unsigned short SPI_ReceiveData()
{
	unsigned short data = 0x00;
	data = spi_soft_obj.data[SOFT_SPI_DATA_IN];
	spi_soft_obj.data[SOFT_SPI_DATA_IN] = 0;

	
	return data;
}

这样就完成了一个SPI器件的建模。替换引脚只需要替换宏定义与初始化的引脚位置即可。

笔者使用梦源的逻辑分析仪就可以直接得到现在的测试结果,下图是25KHz的具体情况

image20211219230649325.png

可以看到,当前字符累加结果无误,可以使用。


标题:记:模拟SPI的相关介绍与实际效果
作者:GreenDream
地址:HTTPS://greendreamer.work/articles/2021/12/22/1640132323325.html
  • 版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明出处!