欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

STM32F407用wk2124芯片编写SPI转四路串口驱动

程序员文章站 2024-02-23 21:06:16
...

因为项目用到了wk2124芯片来进行串口扩展,网上找了好多资料没有现成的,根据商家提供的demo,它是基于103写的,所以根据自己板子的实际情况进行了改写,并且学习一下里面的主要函数及我对函数的理解

 

原理图

STM32F407用wk2124芯片编写SPI转四路串口驱动

 用到5个引脚,片选CS、时钟SCK、MOSI、MISO和中断IRQ,因为RST设置的是板子连电后电容充电,所以会自动复位,不用这个引脚。

一. SPI

1.GPIO初始化设置

  GPIO_InitTypeDef GPIO_InitStructure;

  RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE);//使能GPIOB时钟
  RCC_APB1PeriphClockCmd(RCC_APB1Periph_SPI2, ENABLE);//使能SPI2时钟,在APB1时钟里面
  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13|GPIO_Pin_14|GPIO_Pin_15;
  //PB13-15分别复用为SCK,MISO,MOSI
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;//复用功能
  GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;//推挽输出
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;//100MHz
  GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;//上拉
  GPIO_Init(GPIOB, &GPIO_InitStructure);//初始化

  GPIO_SetBits(GPIOB,GPIO_Pin_13|GPIO_Pin_14|GPIO_Pin_15);

2.SPI参数配置

STM32F407用wk2124芯片编写SPI转四路串口驱动

 

 

STM32F407用wk2124芯片编写SPI转四路串口驱动

 

因为发送接收是同时进行,所以选择全双工,SPI设置为主模式 ,大小为8bit,时钟默认拉低,第二个时钟沿开始捕捉,数据格式SPI_FirstBit(选择是MSB还是LSB),选择MSB方式,举个例子假设用16bit,就是[15,0]这样,如果是LSB就是[0,15]这样。


	SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex;  //SPI设置为双线双向全双工
	SPI_InitStructure.SPI_Mode = SPI_Mode_Master;		//设置为主SPI
	SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b;		//SPI发送接收8位帧结构
	SPI_InitStructure.SPI_CPOL = SPI_CPOL_Low;		//时钟悬空低
	SPI_InitStructure.SPI_CPHA = SPI_CPHA_2Edge;	//数据捕获于第二个时钟沿
	SPI_InitStructure.SPI_NSS = SPI_NSS_Soft;		//NSS信号由软件(使用SSI位)管理
	SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_256;	//定义波特率预分频的值:波特率预分频值为256
	SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;	//数据传输从MSB位开始
	SPI_InitStructure.SPI_CRCPolynomial = 7;	//CRC值计算的多项式
	SPI_Init(SPI2, &SPI_InitStructure);  //根据SPI_InitStruct中指定的参数初始化外设SPIx寄存器
 
	SPI_Cmd(SPI2, ENABLE); //使能SPI外设
	SPI2_ReadWriteByte(0xff);//启动传输

3.读写函数

u8 SPI2_ReadWriteByte(u8 TxData)
{		
	u8 retry=0;				 	
	while (SPI_I2S_GetFlagStatus(SPI2, SPI_I2S_FLAG_TXE) == RESET) //发送缓存空标志位
		{
		retry++;
		if(retry>200)return 0;
		}			  
	SPI_I2S_SendData(SPI2, TxData); //通过外设SPIx发送一个数据
	retry=0;

	while (SPI_I2S_GetFlagStatus(SPI2, SPI_I2S_FLAG_RXNE) == RESET)//接受缓存非空标志位
		{
		retry++;
		if(retry>200)return 0;
		}	  						    
	return SPI_I2S_ReceiveData(SPI2); //返回通过SPIx最近接收的数据					    
}

读写函数中,用SPI_I2S_GetFlagStatus不断去获取TXE和RXNE的状态,如果是非空就继续,如果为空就调用Send和Rece函数去发送接收数据,当retry标志连续超过200次,就返回0,发送/接收失败。

4.速度设置

void SPI2_SetSpeed(u8 SpeedSet)
{
	SPI_InitStructure.SPI_BaudRatePrescaler = SpeedSet ;
    SPI_Init(SPI2, &SPI_InitStructure);
	SPI_Cmd(SPI2,ENABLE);
} 

 

二、WK2124

引脚对应关系

CS    PB12
SCK   PB13
MOSI  PB15
MISO  PB14
IRQ   PB1

 

1.片选初始化

void SPI_CS_Init(void)
{
 GPIO_InitTypeDef  GPIO_InitStructure;
 RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE);		 //使能PB端口时钟
 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12;	//PB12端口配置
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;//输出
 GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;//推挽输出	
 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;		 //IO口速度为100MHz
 GPIO_Init(GPIOB, &GPIO_InitStructure);					 //根据设定参数初始化GPIOB12
 GPIO_SetBits(GPIOB,GPIO_Pin_12);						 //PB12 输出高	
}

 

2. 写寄存器函数

void WK2124WriteReg(unsigned char port,unsigned char reg,unsigned char dat)
{	 
	 SPI_CS_L();//片选使能
	 SPI2_ReadWriteByte(((port-1)<<4)+reg); //写控制字节
	 SPI2_ReadWriteByte(dat); //写数据
	 SPI_CS_H();//片选无效
}

首先把片选信号拉低,然后写入控制命令,再写入数据,写完之后再拉高片选,对于其中的控制字节((port-1)<<4)+reg可以根据数据手册得知,port:为子串口的端口号(C0\C1),reg是目标寄存器,dat是数据。

STM32F407用wk2124芯片编写SPI转四路串口驱动

6,7位为默认为0,0,C1,C0为串口通道,可选值为00,01,10,11分别表示4个串口,默认设置为00,通过传入参数port(port设置的是1,2,3,4,但是因为从0开始,所以要port-1)来进行改变,然后左移4位变成00 0000,然后加上初始的默认值0000 0000,最后加上要写入的寄存器,所以其实是0x00+(port-1)<<4)+reg,然后将这个cmd传入spi读写函数进行读写。

 

3. 读寄存器函数

unsigned char WK2124ReadReg(unsigned char port,unsigned char reg)
{	
	unsigned char rec_data; 
	SPI_CS_L();	//片选使能
	SPI2_ReadWriteByte(0x40+((port-1)<<4)+reg);//写控制字节
	rec_data=SPI2_ReadWriteByte(0);//接收返回的数据
	SPI_CS_H();	//片选无效	
	return rec_data;
}

4. 写FIFO函数

void WK2124WriteFifo(unsigned char port,unsigned char *wbuf,unsigned int len)
{	 unsigned char n;
	 SPI_CS_L(); // 片选有效
	 SPI2_ReadWriteByte(0x80+((port-1)<<4)); //写FIFO控制指令
	  for(n=0;n<len;n++)
	  {
	     SPI2_ReadWriteByte(*(wbuf+n));
		} 
	 SPI_CS_H();	//片选无效

}

  port:为子串口的端口号(C0\C1), *wbuf:写入数据部分, len:  写入数据长度。

大部分和读写寄存器相同,主要控制字节不同,其次可以一次写入多个数据。

STM32F407用wk2124芯片编写SPI转四路串口驱动

控制字节的变化主要是初始SPI的CMD为1000 0000,也就是0x80,其他一样。

 

5.读FIFO函数

void WK2124WriteFifo(unsigned char port,unsigned char *wbuf,unsigned int len)
{	 unsigned char n;
	 SPI_CS_L(); // 片选有效
	 SPI2_ReadWriteByte(0x80+((port-1)<<4)); //写FIFO控制指令
	  for(n=0;n<len;n++)
	  {
	     SPI2_ReadWriteByte(*(wbuf+n));
		} 
	 SPI_CS_H();	//片选无效

}

 

6. WK2124初始化函数

首先使能子串口时钟


    unsigned char gena,grst,gier,sier,scr;
	//使能子串口时钟
    gena=WK2124ReadReg(WK2124_GPORT,WK2124_GENA); 
	switch (port)
    {
          case 1://使能子串口1的时钟
              gena|=WK2124_UT1EN;
		      WK2124WriteReg(WK2124_GPORT,WK2124_GENA,gena);
              break;
		  case 2://使能子串口2的时钟
              gena|=WK2124_UT2EN;
		      WK2124WriteReg(WK2124_GPORT,WK2124_GENA,gena);
              break;
		   case 3://使能子串口3的时钟
              gena|=WK2124_UT3EN;
		      WK2124WriteReg(WK2124_GPORT,WK2124_GENA,gena);
              break;
		   case 4://使能子串口4的时钟
              gena|=WK2124_UT4EN;
		      WK2124WriteReg(WK2124_GPORT,WK2124_GENA,gena);
              break;
	 }	
	

STM32F407用wk2124芯片编写SPI转四路串口驱动

在头文件中对GENA全局寄存器进行了宏定义:

#define 	WK2124_UT4EN	0x08
#define 	WK2124_UT3EN	0x04
#define 	WK2124_UT2EN	0x02
#define 	WK2124_UT1EN	0x01

根据数据手册说明,低位的0,1,2,3位置1,就使能哪个子串口,如ut2使能就是0000 0010,也就是0x02。

接下来进行串口复位

//软件复位子串口
	grst=WK2124ReadReg(WK2124_GPORT,WK2124_GRST); 
	switch (port)
    {
          case 1://软件复位子串口1
              grst|=WK2124_UT1RST;
		      WK2124WriteReg(WK2124_GPORT,WK2124_GRST,grst);
              break;
		  case 2://软件复位子串口2
              grst|=WK2124_UT2RST;
		      WK2124WriteReg(WK2124_GPORT,WK2124_GRST,grst);
              break;
		   case 3://软件复位子串口3
              grst|=WK2124_UT3RST;
		      WK2124WriteReg(WK2124_GPORT,WK2124_GRST,grst);
              break;
		   case 4://软件复位子串口4
             grst|=WK2124_UT4RST;
		      WK2124WriteReg(WK2124_GPORT,WK2124_GRST,grst);
              break;
	 }	
 

STM32F407用wk2124芯片编写SPI转四路串口驱动

与上面一样,第四位哪个置1,表示哪个子串口进行复位。 

接下来使能串口中断

 //使能子串口中断,包括子串口总中断和子串口内部的接收中断,和设置中断触点
	gier=WK2124ReadReg(WK2124_GPORT,WK2124_GIER); 
	switch (port)
    {
          case 1://子串口1中断使能
              gier|=WK2124_UT1IE;
		      WK2124WriteReg(WK2124_GPORT,WK2124_GIER,gier);
              break;
		  case 2://子串口2中断使能
              gier|=WK2124_UT2IE;
		      WK2124WriteReg(WK2124_GPORT,WK2124_GIER,gier);
              break;
		   case 3://子串口3中断使能
              gier|=WK2124_UT3IE;
		      WK2124WriteReg(WK2124_GPORT,WK2124_GIER,gier);
              break;
		   case 4://子串口4中断使能
              gier|=WK2124_UT4IE;
		      WK2124WriteReg(WK2124_GPORT,WK2124_GIER,gier);
              break;
	 }	 
	

STM32F407用wk2124芯片编写SPI转四路串口驱动

寄存器设置与上面一样。

     //使能子串口接收触点中断和超时中断
	 sier=WK2124ReadReg(port,WK2124_SIER); 
	 sier |= WK2124_RFTRIG_IEN;
	 WK2124WriteReg(port,WK2124_SIER,sier);
	 // 初始化FIFO和设置固定中断触点
	 WK2124WriteReg(port,WK2124_FCR,0XFF);
	 //设置任意中断触点,如果下面的设置有效,那么上面FCR寄存器中断的固定中断触点将失效
	 WK2124WriteReg(port,WK2124_SPAGE,1);//切换到page1
	 WK2124WriteReg(port,WK2124_RFTL,0X08);//设置接收触点8个字节
	 WK2124WriteReg(port,WK2124_TFTL,0X10);//设置发送触点为16个字节
	 WK2124WriteReg(port,WK2124_SPAGE,0);//切换到page0 
	 //使能子串口的发送和接收使能
	 scr=WK2124ReadReg(port,WK2124_SCR); 
	 scr|=WK2124_TXEN|WK2124_RXEN;
	 WK2124WriteReg(port,WK2124_SCR,scr);

 

STM32F407用wk2124芯片编写SPI转四路串口驱动

STM32F407用wk2124芯片编写SPI转四路串口驱动

剩下的这些触点,页面设置,使能都一样,根据数据手册进行设置即可。

 

7.wk2124关闭及复位函数

void WK2124Close(unsigned char port)
{
    unsigned char gena,grst;
	//复位子串口
	grst=WK2124ReadReg(WK2124_GPORT,WK2124_GRST); 
	switch (port)
    {
          case 1://软件复位子串口1
              grst|=WK2124_UT1RST;
		      WK2124WriteReg(WK2124_GPORT,WK2124_GRST,grst);
              break;
		  case 2://软件复位子串口2
              grst|=WK2124_UT2RST;
		      WK2124WriteReg(WK2124_GPORT,WK2124_GRST,grst);
              break;
		   case 3://软件复位子串口3
              grst|=WK2124_UT3RST;
		      WK2124WriteReg(WK2124_GPORT,WK2124_GRST,grst);
              break;
		   case 4://软件复位子串口4
              grst|=WK2124_UT4RST;
		      WK2124WriteReg(WK2124_GPORT,WK2124_GRST,grst);
              break;
	 }	
	//关闭子串口时钟
    gena=WK2124ReadReg(WK2124_GPORT,WK2124_GENA); 
	switch (port)
    {
          case 1://使能子串口1的时钟
              gena&=~WK2124_UT1EN;
		      WK2124WriteReg(WK2124_GPORT,WK2124_GENA,gena);
              break;
		  case 2://使能子串口2的时钟
              gena&=~WK2124_UT2EN;
		      WK2124WriteReg(WK2124_GPORT,WK2124_GENA,gena);
              break;
		   case 3://使能子串口3的时钟
              gena&=~WK2124_UT3EN;
		      WK2124WriteReg(WK2124_GPORT,WK2124_GENA,gena);
              break;
		   case 4://使能子串口4的时钟
              gena&=~WK2124_UT4EN;
		      WK2124WriteReg(WK2124_GPORT,WK2124_GENA,gena);
              break;
	 }	
}

先对GRST寄存器进行操作使其复位,然后利用~运算符关闭串口时钟,同时可以减少参数量。

 

8. 波特率设置函数

void WK2124SetBaud(unsigned char port,int baud)
{  
	unsigned char baud1,baud0,pres,scr;
	//如下波特率相应的寄存器值,是在外部时钟为11.0592的情况下计算所得,如果使用其他晶振,需要重新计算
	switch (baud) 
	{
      case 600:
			baud1=0x4;
			baud0=0x7f;
			pres=0;
      break;
      case 1200:
			baud1=0x2;
			baud0=0x3F;
			pres=0;
			break;
      case 2400:
			baud1=0x1;
			baud0=0x1f;
			pres=0;
			break;
      case 4800:
			baud1=0x00;
			baud0=0x8f;
			pres=0;
			break;
      case 9600:
			baud1=0x00;
			baud0=0x47;
			pres=0;
			break;
      case 19200:
			baud1=0x00;
			baud0=0x23;
			pres=0;
			break;
      case 38400:
			baud1=0x00;
			baud0=0x11;
			pres=0;
			break;
			
      case 76800:
			baud1=0x00;
			baud0=0x08;
			pres=0;
			break; 
       
      case 1800:
			baud1=0x01;
			baud0=0x7f;
			pres=0;
			break;
      case 3600:
			baud1=0x00;
			baud0=0xbf;
			pres=0;
			break;
      case 7200:
			baud1=0x00;
			baud0=0x5f;
			pres=0;
			break;
      case 14400:
			baud1=0x00;
			baud0=0x2f;
			pres=0;
			break;
      case 28800:
			baud1=0x00;
			baud0=0x17;
			pres=0;
			break;
      case 57600:
			baud1=0x00;
			baud0=0x0b;
			pres=0;
      break;
      case 115200:
			baud1=0x00;
			baud0=0x05;
			pres=0;
			break;
      case 230400:
			baud1=0x00;
			baud0=0x02;
			pres=0;
			break;
      default:
			baud1=0x00;
			baud0=0x00;
			pres=0;
    }
	
	//关掉子串口收发使能
	scr=WK2124ReadReg(port,WK2124_SCR); 
	WK2124WriteReg(port,WK2124_SCR,0);
	//设置波特率相关寄存器
	WK2124WriteReg(port,WK2124_SPAGE,1);//切换到page1
	WK2124WriteReg(port,WK2124_BAUD1,baud1);
	WK2124WriteReg(port,WK2124_BAUD0,baud0);
	WK2124WriteReg(port,WK2124_PRES,pres);
	WK2124WriteReg(port,WK2124_SPAGE,0);//切换到page0 
	//使能子串口收发使能
	WK2124WriteReg(port,WK2124_SCR,scr);
	
	
}

计算公式:

STM32F407用wk2124芯片编写SPI转四路串口驱动

Reg整数部分减一并换算成16进制写入{BAUD1,BAUA0};如果还有小数部分,则小数部分*16,然后4舍5入后取整写入PRES。如果没有小数部分,只需把整数部分写入 { BAUD1,BAUA0},PRES写入0即可。

例子:

STM32F407用wk2124芯片编写SPI转四路串口驱动

根据具体晶振去设置即可。

 

8.发送字节函数

unsigned int WK2124SendBuf(unsigned char port,unsigned char *sendbuf,unsigned int len)
{
	 unsigned int ret,tfcnt,sendlen;
	 unsigned char  fsr;
	  
	 fsr=WK2124ReadReg(port,WK2124_FSR);
	 if(~fsr&WK2124_TFULL )//子串口发送FIFO未满
	 {

	     tfcnt=WK2124ReadReg(port,WK2124_TFCNT);//读子串口发送fifo中数据个数
		 sendlen=256-tfcnt;//FIFO能写入的最多字节数
		 
		 if(sendlen<len)
		 {
			ret=sendlen; 
			WK2124WriteFifo(port,sendbuf,sendlen);
		 }
		 else
		 {
			 WK2124WriteFifo(port,sendbuf,len);
			 ret=len;
		 }
	  }
	 
	 return ret;
}

 

9. 接收数据函数

unsigned int WK2124GetBuf(unsigned char port,unsigned char *getbuf)
{
	unsigned int ret=0,rfcnt;
	unsigned char fsr;
	fsr=WK2124ReadReg(port,WK2124_FSR);
	if(fsr&WK2124_RDAT )//子串口发送FIFO未满
	  {
	     rfcnt=WK2124ReadReg(port,WK2124_RFCNT);//读子串口发送fifo中数据个数
		 if(rfcnt==0)//当RFCNT寄存器为0的时候,有两种情况,可能是256或者是0,这个时候通过FSR来判断,如果FSR显示接收FIFO不为空,就为256个字节
		 {rfcnt=256;}
		 WK2124ReadFifo(port,getbuf,rfcnt);
		 ret=rfcnt;
	   }
	 return ret;	
}

最后有一点,UART端口输出为TTL电平,如果需要232或485电平的,需要加相应的电平转换芯片。

 

 

 

这个驱动的大部分代码可以复用,F103和F4的都写了,源码连带数据手册,原理图,linux驱动什么的一会一起打包上传,没有积分的可以留下邮箱,我看见了就会发的,如果有什么疑问或者我哪里写的不对可以留言一起沟通。