替换掉NO_OS逻辑和代码中的SPI部分

28 篇文章 11 订阅
25 篇文章 50 订阅

在纯粹逻辑的配置中我写一个外设,进行SPI接口的读写。几乎是跟AD9361定制的SPI接口:发送三个字节,接收到一个字节。这个小控制器为了简单,只实现对一个AD9361寄存器的读写。我打算将这个一直到NO-OS那套软件硬件代码里面这样减少对ZYNQ硬件外设的依赖,使得这套C代码更容易移植。

关于AD9361中SPI的时序,在接口手册上明确写了,并给出了波形和时序。

这里贴出来部分这个控制器的部分VERILOG代码:



module spi_ad9361_if#(parameter cnt_bit=2)(
        input clk,rst,
        output reg spi_mosi,spi_clk,spi_csn,
        input spi_miso,
        input [23:0] din,
        output reg [7:0] dout=0 ,
        input valid ,
        output reg  ready
    );  
 

                
    initial {spi_mosi,spi_clk,spi_csn,ready} = 0 ;
    reg [7:0]st = 0 ;
    wire is_delay_state =1;
    reg [1:0]  c = 0 ;always @ (posedge clk) if (is_delay_state) c<=c+1;else c<=0;
    wire sync =  c == 2'b11 ;

    reg [23:0]  reg24=0 ;
    reg [7:0]   reg8=0;

    always@(posedge clk)if (rst)st<=0;else
    case (st)
        0  : begin st <= 1 ;  ready<=0;  spi_csn<=1;  spi_clk<=0; end
        1  : if (valid)begin st<=2;  ready<=0;end
        2  : begin reg24<=din ; st <= 100 ;end
        100 : if (sync) begin st <= 200; spi_csn<=0;spi_clk<=0; end //spi_csn=0;
        200 : if (sync) begin st <= 201 ; spi_clk<=1;   spi_mosi<=reg24[23];      reg24[23:0]<={reg24[23:0],1'b0} ;    end  201 : if (sync) begin st <= 202 ; spi_clk<=0;           end  // bit 23
        202 : if (sync) begin st <= 203 ; spi_clk<=1;   spi_mosi<=reg24[23];      reg24[23:0]<={reg24[23:0],1'b0} ;    end  203 : if (sync) begin st <= 204 ; spi_clk<=0;           end  // bit 22
        204 : if (sync) begin st <= 205 ; spi_clk<=1;   spi_mosi<=reg24[23];      reg24[23:0]<={reg24[23:0],1'b0} ;    end  205 : if (sync) begin st <= 206 ; spi_clk<=0;           end  // bit 21
        206 : if (sync) begin st <= 207 ; spi_clk<=1;   spi_mosi<=reg24[23];      reg24[23:0]<={reg24[23:0],1'b0} ;    end  207 : if (sync) begin st <= 208 ; spi_clk<=0;           end  // bit 20
        208 : if (sync) begin st <= 209 ; spi_clk<=1;   spi_mosi<=reg24[23];      reg24[23:0]<={reg24[23:0],1'b0} ;    end  209 : if (sync) begin st <= 210 ; spi_clk<=0;           end  // bit 19
        210 : if (sync) begin st <= 211 ; spi_clk<=1;   spi_mosi<=reg24[23];      reg24[23:0]<={reg24[23:0],1'b0} ;    end  211 : if (sync) begin st <= 212 ; spi_clk<=0;           end  // bit 18
 //此处删节
        240 : if (sync) begin st <= 241 ; spi_clk<=1;   spi_mosi<=reg24[23];      reg24[23:0]<={reg24[23:0],1'b0} ;    end  241 : if (sync) begin st <= 242 ; spi_clk<=0; reg8[3] <= spi_miso;   end  // bit 3
        242 : if (sync) begin st <= 243 ; spi_clk<=1;   spi_mosi<=reg24[23];      reg24[23:0]<={reg24[23:0],1'b0} ;    end  243 : if (sync) begin st <= 244 ; spi_clk<=0; reg8[2] <= spi_miso;   end  // bit 2
        244 : if (sync) begin st <= 245 ; spi_clk<=1;   spi_mosi<=reg24[23];      reg24[23:0]<={reg24[23:0],1'b0} ;    end  245 : if (sync) begin st <= 246 ; spi_clk<=0; reg8[1] <= spi_miso;   end  // bit 1
        246 : if (sync) begin st <= 247 ; spi_clk<=1;   spi_mosi<=reg24[23];      reg24[23:0]<={reg24[23:0],1'b0} ;    end  247 : if (sync) begin st <= 248 ; spi_clk<=0; reg8[0] <= spi_miso;   end  // bit 0
        248 : if (sync) begin st <= 249 ; spi_csn<=1;       end
        249 : if (sync) begin st <= 250 ;       end
        250 : begin st<=251;ready<=1;dout<=reg8;end
        251 : st<=1;
        default st <= 0 ;
    endcase

endmodule

 

很显然这是一个聪明人用本办法写的状态机。运行效果非常好。

做成AXI_LITE接口时候顺便加上了其他一些对外AD9361控制的信号,不使用情况下可以悬空就行。

这里的控制也非常简洁。对应操作这部分SPI控制器的代码如下:


#define CFG_SPI_ACC_ADDR  0X43C00000

unsigned char cfg_spi_acc(unsigned int cmd){ //added by liwei
unsigned int r;
*(volatile unsigned int * ) CFG_SPI_ACC_ADDR = cmd ;
while(1){
	r = *(volatile unsigned int * )CFG_SPI_ACC_ADDR ;
	if (r&(1<<31))break;
}
return r&0xff ;
}

这里读和写都是这个函数。执行写操作时候,忽略返回数值就可以。

 

 

接下来我们看SDK项目里面要移植的接口部分。

 

我们先实现通过此SPI控制器对单个AD9361寄存器的读写实现:


/**
 * SPI register read.
 * @param spi
 * @param reg The register address.
 * @return The register value or negative error code in case of failure.
 */
int32_t ad9361_spi_read(struct spi_device *spi, uint32_t reg)
{
    unsigned int cmd ;
    cmd = AD_READ | AD_CNT(1) | AD_ADDR(reg);
    cmd<<=8;
    return  0xff & cfg_spi_acc(cmd);
}

 


/**
 * SPI register write.
 * @param spi
 * @param reg The register address.
 * @param val The value of the register.
 * @return 0 in case of success, negative error code otherwise.
 */
int32_t ad9361_spi_write(struct spi_device *spi,
			 uint32_t reg, uint32_t val)
{

	int32_t  cmd;

	cmd = AD_WRITE | AD_CNT(1) | AD_ADDR(reg);
	cmd <<=8;
	cmd |= val&0xff;

	if (reg==6)	printf("lw:reg[%d]=0x%02x\n",reg,(unsigned char )val);
	if (reg==7)	printf("lw:reg[%d]=0x%02x\n",reg,(unsigned char )val);

cfg_spi_acc(cmd);

#ifdef _DEBUG
	dev_dbg(&spi->dev, "%s: reg 0x%"PRIX32" val 0x%X", __func__, reg, buf[2]);
#endif

	return 0;
}

移植后的代码非常简洁明了。

 

原来的SDK代码里面实现了连续对多个寄存器进行读写,而我们这里只有对一个寄存进行读写的控制器外设,所以要现在C语言层面把对多个寄存器进行读写的代码转换成调用对一个寄存器进行读写的实现。

读9316的接口文档,可以看到关于连续多寄存器写的描述。在我们采用的是MSB方式先(就是SPI 先传输字节的最高位 Most Signifiaenr Bit )的情况下 ,寄存器地址是递减的,如果要写从8开始的三个寄存器,就是依次写8,7,6。这点一定注意!

 

/**
 * SPI multiple bytes register write.
 * @param spi
 * @param reg The register address.
 * @param tbuf The data buffer.
 * @param num The number of bytes to read.
 * @return 0 in case of success, negative error code otherwise.
 */
static int32_t ad9361_spi_writem(struct spi_device *spi,
				 uint32_t reg, uint8_t *tbuf, uint32_t num)
{
	uint8_t buf[10];
	int32_t ret;
	uint16_t cmd;
	int32_t i;
	if (num > MAX_MBYTE_SPI)
		return -EINVAL;

	for(i=0;i<num;++i)
		ad9361_spi_write( spi,  reg - i ,tbuf[i]);

#ifdef _DEBUG
	{

		for (i = 0; i < num; i++)
			dev_dbg(&spi->dev, "Reg 0x%"PRIX32" val 0x%X", reg--, tbuf[i]);
	}
#endif

	return 0;
}

 

ad9361_spi_writem这个函数是对多个寄存器进行写。ad9361_spi_write这个函数是对个寄存器进行写。我们将读多个寄存器的连续写改成了调用ad9361_spi_write函数多多个寄存器的分别写。 这里注意的是连续写方式下,是先写高地址的寄存,所以这里的索引变量i作为减数。

 

/**
 * SPI multiple bytes register read.
 * @param spi
 * @param reg The register address.
 * @param rbuf The data buffer.
 * @param num The number of bytes to read.
 * @return 0 in case of success, negative error code otherwise.
 */
int32_t ad9361_spi_readm(struct spi_device *spi, uint32_t reg,
			 uint8_t *rbuf, uint32_t num)
{

	int32_t i;

	for(i=0;i<num;++i)	rbuf[i]=ad9361_spi_read(spi,reg-i);

#ifdef _DEBUG
	{
		for (i = 0; i < num; i++)
			dev_dbg(&spi->dev, "%s: reg 0x%"PRIX32" val 0x%X",
				__func__, reg--, rbuf[i]);
	}
#endif

	return 0;
}

这里的ad9361_spi_readm,我们改成了多次调用ad9361_spi_read来实现。注意在原来的SDK代码里面ad9361_spi_read的实现是调用ad9361_spi_readm实现的,可以这样做的道理很简单一个就是多个的特例嘛。我这里已经修改了ad9361_spi_read代码成了驱动自己的控制器。

 

这些工作都做完了之后,从新编译运行,一切OK!

 

运行单音测试选取一路组成波形。完全OK.

 

 

 

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值