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的具体情况
可以看到,当前字符累加结果无误,可以使用。
标题:记:模拟SPI的相关介绍与实际效果
作者:GreenDream
地址:HTTPS://greendreamer.work/articles/2021/12/22/1640132323325.html
Comments | 0 条评论