-->
Page 1 of 1

[solved]About the SPI clock dividing

PostPosted: Wed Dec 03, 2014 1:59 am
by qdk0901
Hi, everyone. I'm using the ESP8266 chip to drive the LED Strip that using ucs1903 controller. The ucs1903 controller use a single line communication protocol, and I have implemented a version based on GPIO bit-banging, now I'm going to use the SPI to simulate the timming to make things more efficient. But when I refer to the official HSPI demo, http://bbs.espressif.com/viewtopic.php?f=15&t=56&p=198&hilit=hspi#p198, I was confused on the SPI clock dividing. I have make some tries as bellow:

void ICACHE_FLASH_ATTR
ucs1903_spi_init()
{
uint32 regvalue;


//refer to http://www.esp8266.com/viewtopic.php?t=669&p=3624
//bit9 in PERIPHS_IO_MUX should decide whether SPI clock is derived from system clock
//but nothing is change by clear bit9 or set it.

WRITE_PERI_REG(PERIPHS_IO_MUX, 0x105); //clear bit9

//PIN_FUNC_SELECT(PERIPHS_IO_MUX_MTDI_U, 2);//configure io to spi mode
//PIN_FUNC_SELECT(PERIPHS_IO_MUX_MTCK_U, 2);//configure io to spi mode
//PIN_FUNC_SELECT(PERIPHS_IO_MUX_MTMS_U, 2);//configure io to spi mode
PIN_FUNC_SELECT(PERIPHS_IO_MUX_MTDO_U, 2);//configure io to spi mode


SET_PERI_REG_MASK(SPI_USER(1), SPI_CS_SETUP | SPI_CS_HOLD | SPI_USR_COMMAND | SPI_USR_MOSI);
CLEAR_PERI_REG_MASK(SPI_USER(1), SPI_FLASH_MODE);

//clear Daul or Quad lines transmission mode
CLEAR_PERI_REG_MASK(SPI_CTRL(1), SPI_QIO_MODE |SPI_DIO_MODE | SPI_DOUT_MODE | SPI_QOUT_MODE);


//I send out 0xaa 0x55 in ucs1903_spi_test function to genertate a square wave on SPI MOSI pin
//I make some changes here on the SPI clock dividing configuration register and observe changes on the SPI MOSI pin
//When I change the value of N field in the register,
//N == 3, H == 1, L == 3, SPI MOSI output 555.5kHz square wave signal
//N == 2, H == 1, L == 3, SPI MOSI output 634.8kHz square wave signal
//N == 1, H == 1, L == 3, SPI MOSI output 740.6kHz square wave signal
//N == 0, SPI MOSI always high
//When N == 3, H and L are non zero value, the SPI MOSI always output 555.5kHz square wave signal
// so I wonder is anyone here know about the meaning of each field in the SPI clock dividing register and kind to tell me how can I get a 4MHz SPI clock here. Thanks.

WRITE_PERI_REG(SPI_CLOCK(1),
((0 & SPI_CLKDIV_PRE) << SPI_CLKDIV_PRE_S) |
((3 & SPI_CLKCNT_N) << SPI_CLKCNT_N_S) |
((1 & SPI_CLKCNT_H) << SPI_CLKCNT_H_S) |
((3 & SPI_CLKCNT_L) << SPI_CLKCNT_L_S)); //clear bit 31,set SPI clock div

//set 8bit output buffer length, the buffer is the low 8bit of register"SPI_FLASH_C0"
WRITE_PERI_REG(SPI_USER1(1),
((7 & SPI_USR_MOSI_BITLEN) << SPI_USR_MOSI_BITLEN_S)|
((7 & SPI_USR_MISO_BITLEN) << SPI_USR_MISO_BITLEN_S));
}

void ICACHE_FLASH_ATTR
ucs1903_spi_send(uint8 data)
{
uint32 regvalue;

while(READ_PERI_REG(SPI_CMD(1)) & SPI_USR);

CLEAR_PERI_REG_MASK(SPI_USER(1), SPI_USR_MOSI | SPI_USR_MISO);

//SPI_FLASH_USER2 bit28-31 is cmd length,cmd bit length is value(0-15)+1,
// bit15-0 is cmd value.
WRITE_PERI_REG(SPI_USER2(1), ((7 & SPI_USR_COMMAND_BITLEN) << SPI_USR_COMMAND_BITLEN_S) | data);

SET_PERI_REG_MASK(SPI_CMD(1), SPI_USR);
}

void ICACHE_FLASH_ATTR
ucs1903_spi_test()
{
while(1) {
ucs1903_spi_send(0xaa);
ucs1903_spi_send(0x55);
}
}

Re: About the SPI clock dividing

PostPosted: Fri Dec 05, 2014 4:44 pm
by TheLastMutt
Hi!

qdk0901 wrote:
//refer to http://www.esp8266.com/viewtopic.php?t=669&p=3624
//bit9 in PERIPHS_IO_MUX should decide whether SPI clock is derived from system clock
//but nothing is change by clear bit9 or set it.

WRITE_PERI_REG(PERIPHS_IO_MUX, 0x105); //clear bit9


In my experiments bit9 only changes the behavior of the SPI Clock signal. MOSI is not affected. I would keep it cleared unless you need 80MHz SPI clock signal.

qdk0901 wrote: //I send out 0xaa 0x55 in ucs1903_spi_test function to genertate a square wave on SPI MOSI pin

...

// so I wonder is anyone here know about the meaning of each field in the SPI clock dividing register and kind to tell me how can I get a 4MHz SPI clock here. Thanks.


I am not sure now if you now need 4MHz at MOSI when transmitting 0xAA (= 8MHz SPI clock) or 4MHz SPI clock signal (= 2MHz at MOSI)?

The CLKCNT_H and CLKCNT_L bits are still mysterious to me. They somehow change the duty cycle (and maybe phase?) of the SPI Clock signal.

SPI_CLKDIV_PRE is a clock prescaler. If you set it to value N then 80MHz system clock is divided by N+1. Setting it to 0 is probably not a good idea. The bit called SPI_CLK_EQU_SYSCLK is probably used to bypass the prescaler instead.

CLKCNT_N defines the number of prescaled clock cycles per bit. The actual number of cycles is CLKCNT_N+1.

So if you want to generate 4MHz at MOSI, you need 8MHz bit rate. This can be achieved by dividing the 80MHz clock by 2 (80MHz / 2 = 40MHz) and then using 5 cycles per bit (40MHz / 5 = 8MHz).
So I we don't touch CLKCNT_H and CLKCNT_L, set SPI_CLKDIV_PRE to 1 and CLKCNT_N to 4 we get

Code: Select allWRITE_PERI_REG(SPI_FLASH_CLOCK(spi_no), 0x00044043);


0x00044043.png

Channel 3 is SPI clock, channel 1 is MOSI.

To generate 2MHz at MOSI (4MHz bit rate) you can keep the divider at 2 and increase the cycles per bit to 10 (80MHz / 2 / 10 = 4MHz) or divide by 4 and use 5 cycles per bit (80MHz / 4 / 5 = 4MHz). Then play around with CLKCNT_H and CLKCNT_L to get approx. 50% duty cycle on SPI clock signal.
Setting SPI_FLASH_CLOCK to 0x00049045 got the desired result for me.

Re: About the SPI clock dividing

PostPosted: Sun Jan 25, 2015 3:41 am
by qdk0901
TheLastMutt wrote:
0x00044043.png

Channel 3 is SPI clock, channel 1 is MOSI.

To generate 2MHz at MOSI (4MHz bit rate) you can keep the divider at 2 and increase the cycles per bit to 10 (80MHz / 2 / 10 = 4MHz) or divide by 4 and use 5 cycles per bit (80MHz / 4 / 5 = 4MHz). Then play around with CLKCNT_H and CLKCNT_L to get approx. 50% duty cycle on SPI clock signal.
Setting SPI_FLASH_CLOCK to 0x00049045 got the desired result for me.


Hi, sorry for reply so late. I have tried the SPI_FLASH_CLOCK value 0x00049045, but only got 250.8kHz clock at MOSI, actually I want a clock of 2MHz at MOSI. Would you please post your test code here, I think maybe it the problem of the sending byte function which I copy from SDK 0.9.4, but I can't figure out where go wrong.

Re: [solved]About the SPI clock dividing

PostPosted: Tue Jan 27, 2015 4:52 am
by qdk0901
sorry, it my fault, I make a mistake with MOSI pin, actually it's GPIO13.

use below code can generate 2MHz signal at MOSI pin successfully.
thanks very much to TheLastMutt.
Code: Select allvoid
spi_init()
{
       WRITE_PERI_REG(PERIPHS_IO_MUX, 0x105); //clear bit9

   //PIN_FUNC_SELECT(PERIPHS_IO_MUX_MTDI_U, 2);//configure io to spi mode
   PIN_FUNC_SELECT(PERIPHS_IO_MUX_MTCK_U, 2);//configure io to spi mode MOSI GPIO13
   //PIN_FUNC_SELECT(PERIPHS_IO_MUX_MTMS_U, 2);//configure io to spi mode CLK GPIO14
   //PIN_FUNC_SELECT(PERIPHS_IO_MUX_MTDO_U, 2);//configure io to spi mode CS


   SET_PERI_REG_MASK(SPI_USER(1), SPI_USR_MOSI );  // use data only (no addr, no cmd, no dummy, command/data for LCD is defined by the DC pin low/high status)
   CLEAR_PERI_REG_MASK(SPI_USER(1), SPI_FLASH_MODE | SPI_WR_BYTE_ORDER | SPI_USR_MISO | SPI_USR_ADDR | SPI_USR_COMMAND | SPI_USR_DUMMY);   // big endian (MSB)

   //spi clock =  80 / (3 + 1) / (4 + 1) == 4M
   //mosi = spi clock / 2 = 2M
   WRITE_PERI_REG(SPI_CLOCK(1),
               ((3 & SPI_CLKDIV_PRE) << SPI_CLKDIV_PRE_S) |
               ((4 & SPI_CLKCNT_N) << SPI_CLKCNT_N_S) |
               ((2 & SPI_CLKCNT_H) << SPI_CLKCNT_H_S) |
               ((2 & SPI_CLKCNT_L) << SPI_CLKCNT_L_S)); //clear bit 31,set SPI clock div
               
   SET_PERI_REG_MASK(SPI_USER(1), SPI_USR_MOSI);
   CLEAR_PERI_REG_MASK(SPI_USER(1), SPI_FLASH_MODE | SPI_WR_BYTE_ORDER | SPI_USR_MISO | SPI_USR_ADDR | SPI_USR_DUMMY | SPI_USR_COMMAND);
}

void spi_send(int c)
{
   while (READ_PERI_REG(SPI_CMD(1)) & SPI_USR); //waiting for spi module available
   WRITE_PERI_REG(SPI_USER1(1), (31 & SPI_USR_MOSI_BITLEN) << SPI_USR_MOSI_BITLEN_S);
   WRITE_PERI_REG(SPI_W0(1), c);  // I also tried data << 24 to shift the byte I want to send to the highest/most significant byte of 32 bits of W0 but it did not make a difference
   SET_PERI_REG_MASK(SPI_CMD(1), SPI_USR);   // send
}

void
spi_test()
{
   while(1) {
      spi_send(0xaaaaaaaa);
   }
}