Get Even More Visitors To Your Blog, Upgrade To A Business Listing >>

XMEGA-B1 XPLAINED: USART in MASTER SPI MODE TUTORIAL

In the last tutorial we saw how to use TWI in XMEGA (as Master). In this tutorial we will see USART in master Spi Mode in XMEGA.

ATxmega128B1 USART in master SPI Mode supports following features:

  1. The USART transmitter in master SPI mode includes buffering.
  2. The USART receiver in master SPI mode includes an additional buffer level.
  3. The USART in master SPI mode does not include write collision feature which is availabe with normal SPI.
  4. The USART in master SPI mode does not include the double speed mode which is available in normal SPI.
  5. The USART in master SPI mode have pins as TxD (Master out only), RxD (Master in only), XCK (clock pin). There is no such Slave Select pin for USART in master SPI mode.

I won’t go in detail of USART in master SPI mode. Hope all of you are familiar with it!

XMEGA-B1 XPLAINED board comes with footprint for adding external memory. Fortunately, AT45DB642D is already mounted on my board. The datasheet of AT45DB642D is available here. I have already written a tutorial on SPI which includes code for AT45DB041B, it can be found here. Both AT45DB041B and AT45DB642D are almost similar in case of operation, the main features of AT45DB0642D are highlighted below.

  1. Dual-interface Architecture
  • RapidS Serial Interface: 66 MHz maximum clock frequency

SPI Compatible Modes 0 and 3

  • Rapid8 8-bit Interface: 50MHz Maximum Clock Frequency
  1. User Configurable Page Size
  • 1024 bytes per page
  • 1056 bytes per page
  • Page size can be factory preconfigured for 1024 bytes
  1. Low power dissipation
  • 10 mA Active Read Current Typical – Serial Interface
  • 10 mA Active Read Current Typical – 8-bit Interface
  • 25 uA Standby Current Typical
  • 9 uA Deep Power Down Typical
  1. Hardware and Software Data Protection Features
  • Individual Sector
  1. Permanent Sector Lockdown for Secure Code and Data Storage
  • Individual Sector
  1. Security: 128-byte Security Register
  • 64-byte User Programmable Space
  • Unique 64-byte Device Identifier
  1. JEDEC Standard Manufacturer and Device ID Read
  2. 100,000 Program/Erase Cycles per Page Minimum

AT45DB642D supports following modes of operations:

Read Operations:

  1. Continuous Array Read Mode:

There are three continuous array read modes. All the three modes have different opcodes and number of dummy bytes that are need to be sent to read data differ for all the three modes. First two modes are for high frequencies i.e. up to 66 MHz and last mode is for low frequency mode i.e. up to 33 MHz. In all the three modes, the contents of data buffers remain unchanged.

  1. Main Memory Page Read
  2. Buffer Read

Program/Erase Operations:

  1. Buffer Write
  2. Buffer to Main Memory Page Program with Built-in Erase
  3. Buffer to Main Memory Page Program without Built-in Erase
  4. Page Erase
  5. Block Erase
  6. Sector Erase
  7. Chip Erase
  8. Main Memory Page Program Through Buffer

Sector Protection:

Along with all above read and write operations there are few other commands for sector protection. Sector Protection Register is used in order to specify the sectors, which is to be protected or unprotected.  There are also three more commands available to program, read, erase Sector Protection Register.

Sector protection can be achieved in two ways either by software or by hardware. Software sector protection makes use Enable Sector Protection Command & Disable Software Protection Command.

OTOH, hardware sector protection can be achieved by asserting WP pin. Once WP is asserted, the sectors specified by Sector Protection Register will be protected against any program or erase operation. In case if any program or erase operation command is issued while WP pin is asserted, the corresponding command will be ignored by the device and it will return to IDLE state once the CS pin is deasserted.

Sector Lockdown:

The device comes with another feature called sector lockdown mechanism. This mechanism allows individual sector of the device to be locked permanently so that it becomes read only. Once the sector of the device is locked it cannot be programmed, erased or unlocked again. Similar to all other operations, the sector lockdown operation also achieved with the help of 4 opcodes. In order to lockdown particular sector of the device the Sector Lockdown Register must be written with proper value. During the execution of sector lockdown command if device is powered down then the lockdown of the sector cannot be guaranteed. In this case user should read Sector Lockdown Register to determine status of appropriate status lockdown bits.

Security Register:

The device contains specialized security register which can be used for purposes like unique device serialization or locked key storage. The register comprised of total 128 bytes. Out of these 128 bytes, first 64 bytes are allocated as one time user programmable space. Once these 64 bytes are programmed they cannot be reprogrammed. The remaining 64 bytes (64 to 127 bytes) are factory programmed by Atmel and will contain unique value for each device. These 64 bytes are also fixed and cannot be reprogrammed. To program security register device must be clocked with 4 byte opcode with correct sequence with asserting CS pin first. Like other operations if device is powered down up during program cycle security register, then contents of user programmable 64 bytes of Security Register cannot be guaranteed. The program security register command uses SRAM buffer for processing. Hence the contents of the buffer will be altered from its previous state when this command is issued.

AT45DB642D also supports following additional commands:

  1. Main Memory Page to Buffer Transfer
  2. Main Memory Page to Buffer Compare
  3. Auto Page Rewrite
  4. Status Register Read (which needs to be read successfully prior to any other operation)
  5. Deep Power Down
  6. Resume from Deep Power Down
  7. “Power of 2” Binary Page Size Option:

This register is user programmable non volatile register that allows the page size of main memory to be configured for either 1024 bytes or 1056 bytes. The “Power of 2” register is one time programmable configuration register and once it is configured for Power 2 page size, it cannot be reconfigured again. The devices are initially shipped with page size set to 1056 bytes (The user also has option of ordering device with 1024 bytes page size from factory).

For Power 2 page size to become effective, following steps must be followed:

  • Program one time programmable configuration register using opcode sequence.
  • Power cycle the device (power down and power up again).
  • User can now program the page for binary page size
  1. Manufacturer and Device ID Read

In my case, I have changed page size of the onboard available AT45DB642D to 1024 bytes with the help of Power of 2 command.

This finishes summary of operations that are available with AT45DB642D.

Before moving to actual configuration of master SPI mode in USART, first we need to set the direction of pins for USART in master in SPI mode.


PORTC_DIR|=PIN1_bm|PIN3_bm|PIN5_bm|PIN7_bm; //TXD0, XCK0, MOSI, SCK  as output PORTD_DIR|=PIN2_bm; //dataflash SS pin as output

In order to use USART in master SPI mode we need to move the locations of USART pins to SPI pins. But before that note, we need to swap the locations of SCK and MOSI pins so that after moving USART pins to SPI pin location each and every pin of USART will come at corresponding pin of SPI. This can be achieved with following operations.


PORTC_REMAP|=PORT_SPI_bm; //swap locations of MOSI & SCK pins

PORTC_REMAP|=PORT_USART0_bm; //move pins from PORTC[3:0] to PORTC[7:4]

The default mode of dataflash is mode 3, to configure the USART of our XMEGA in mode 3 we need to set the INVEN of pin number 5 of PORTC. It ca be done as below:


PORTC_PIN5CTRL|=PORT_INVEN_bm; //enable INVEN on pin 5 for SPI mode 3

Let us move towards  the configuration of registers for USART in master SPI mode.

DATA:

The USART transmit data buffer register and receive data buffer register share the same I/O address and is referred to as USART data register.

 

STATUS:

The bits of this register are flags that are set individually on occurrence of particular condition. Those flags are as below:

  1. RXCIF: This flag is set when there are unread data in receive buffer and cleared when receive buffer is empty i.e. it doesn’t contain any data. When interrupt is used this flag is cleared after data is read in receive complete interrupt routine. Similarly this flag can be cleared by writing 1 to this bit.
  1. TXCIF: This flag is set when the entire frame in the transmit shift register has been shifted out and there are no new data in transmit buffer. This flag is cleared when corresponding interrupt vector is executed or it can be cleared by writing 1 to this bit.
  1. DREIF: This flag is one when transmit buffer is empty and zero when transmit buffer contains data to be transmitted and that has not yet been moved into shift register.
  1. FERR: This flag is unused in master SPI mode.
  1. BUFOVF: This flag is unused in master SPI mode.
  1. PERR: This flag is unused in master SPI mode.
  1. RXB8: This bit is unused in master SPI mode.

Hence we will write this register as:


USARTC0_STATUS=USART_RXCIF_bm|USART_TXCIF_bm; //clear receive interrupt flag and transmit interrupt flag

CTRLA:

This register is used to configure interrupt levels of receive complete interrupt, transmit complete interrupt and data register empty interrupt. As we are not using interrupts we won’t configure this register.

CTRLB:

The RXEN and TXEN bits of this register are used to enable receiver and transmitter. The rest of  bits of this register are unused in master SPI mode. We will enable both receiver and transmitter.


USARTC0_CTRLB=USART_RXEN_bm|USART_TXEN_bm; //enable receiver and transmitter

 CTRLC:

This register contains communication mode selection bits, data order bit, clock phase bit. The rest of bits of this register are unused in master SPI mode. Of course, communication mode will be master SPI mode. We will keep data order bit as zero as we want MSB of data word to be transmitted first. The default mode of dataflash is Mode 3, hence clock phase bit will be one.


USARTC0_CTRLC=0xc2; //communication mode master SPI, clock phase bit is one as default mode of dataflash is Mode 3 on power ON

BAUDCTRLA:

This register contains lower 8 bits out of 12 bit BSEL value used for USART baud rate setting.

BAUDCTRLB:

The lower nibble of this register contains upper 4 bits of 12 bit value used for USART baud rate setting. The higher nibble of this register contains baud rate generator scale factor. The scale factor is given 2’s compliment form from -7 (0b1001) to +7 (0b0111). The -8 (0b1000) setting is reserved.

The formulae for baud rate calculation as per datasheet are below.

We will use standard baud rate value as 1MHz for this example. We will select BSCALE value as 0x00 for simplicity. Therefore

BSEL=(fPER/2*fBAUD)-1 = (8*10^6)/(2*1*10^6)-1=0x03

According to above calculations the value of BAUDCTRLA and BAUDCTRLB becomes


USARTC0_BAUDCTRLA=0x03; //as BSEL is 3

USARTC0_BAUDCTRLB=0x00; //as BSCALE is 0

This finishes our master SPI mode initialization of USART.

As mentioned earlier, the operaiton of AT45DB642D is very similar to AT45DB041B. Hence I will show the basic operations of dataflash like status register read, enable/disable sector protection, read JEDEC ID, read sector lockdown register.

I have left the rest of operations as homework for you guys. If you have any doubts for the same you can mention it in comments.

We will use onboard LCD to display the result of different operations  on dataflash.

#include 

#define F_CPU 8000000UL
#include 

//The following set of macros is for 14-segment alphanumeric digits

#define width_14seg 7     //as total segments are 7 from A6 to A0
#define first_14seg 12    //use 12 for left to right as A6-h has first position at 12
                          //use 36 for right to left as A0-h has first position at 36
#define direction_14seg 0 //as direction is incrementing from left to right
                          //use one for right to left i.e. decrementing

//The following set of macros is for 7-segment numeric digits

#define width_7seg 4      //as total segments are 5 from D4 to D0
#define first_7seg 8      //use 2 for right to left as D0-a has first position at 2
                          //use 10 for left to right as D4-a has first position at 10
#define direction_7seg 1  //as direction is decrementing from left to right
                          //use zero for right to left i.e. incrementing

void clock_init(void);
void lcd_init(void);
void init_mspi(void);
unsigned char mspi_xchange(unsigned char tx_byte);
void cs_low(void);
void cs_high(void);
unsigned char read_status(void);
void read_id(void);
void display_alphanumeric(unsigned char *pattern);
void display_numeric(unsigned char *pattern);

unsigned char id1,id2,id3,id4,status,array[3],disp_cmd1[]="STATREG",disp_cmd2[]="JEDECID",disp_cmd3[]="SECLOCK",sector_lockdown[32];

int main(void)
{
 clock_init();
 lcd_init();
 init_mspi();

 //read status register
 cs_low();

 mspi_xchange(0xd7);//read status register opcode
 status=mspi_xchange(0x00);

 cs_high();

 for(signed char count=2;count>=0;count--)
 {
    array[count]=(status%10)|0x30;
    status=(status/10);
 }

 display_alphanumeric(disp_cmd1);
 display_numeric(array);
 _delay_ms(5000);

 //read sector lockdown register
 cs_low();

 mspi_xchange(0x35);
 mspi_xchange(0x00);
 mspi_xchange(0x00);
 mspi_xchange(0x00);

 for(unsigned char byte_count=0;byte_count=0;count--)
    {
       array[count]=(sector_lockdown[byte_count]%10)|0x30;
       sector_lockdown[byte_count]=(sector_lockdown[byte_count]/10);
    }

  display_alphanumeric(disp_cmd3);
  display_numeric(array);
  _delay_ms(5000);
 }

 //read device id
 cs_low();

 mspi_xchange(0x9f); //read device id opcode
 id1=mspi_xchange(0x00); //read second byte
 id2=mspi_xchange(0x00); //read second byte
 id3=mspi_xchange(0x00); //read third  byte
 id4=mspi_xchange(0x00); //read fourth byte

 cs_high();

 display_alphanumeric(disp_cmd2);

 //transmit first ID byte

 for(signed char count=2;count>=0;count--)
 {
    array[count]=(id1%10)|0x30;
    id1=(id1/10);
 }

 display_numeric(array);
 _delay_ms(5000);

 //transmit second ID byte

 for(signed char count=2;count>=0;count--)
 {
    array[count]=(id2%10)|0x30;
    id2=(id2/10);
 }

 display_numeric(array);
 _delay_ms(5000);

 //transmit third ID byte

 for(signed char count=2;count>=0;count--)
 {
    array[count]=(id3%10)|0x30;
    id3=(id3/10);
 }

 display_numeric(array);
 _delay_ms(5000);

 //transmit fourth ID byte

 for(signed char count=2;count>=0;count--)
 {
    array[count]=(id4%10)|0x30;
    id4=(id4/10);
 }

 display_numeric(array);
 _delay_ms(5000);

 //initially sector protection is enabled
 //now send command to disable it
 cs_low();

 mspi_xchange(0x3d);
 mspi_xchange(0x2a);
 mspi_xchange(0x7f);
 mspi_xchange(0x9a);

 cs_high();

 //re-read status register to confirm
 cs_low();

 mspi_xchange(0xd7);//read status register opcode
 status=mspi_xchange(0x00);

 cs_high();

 for(signed char count=2;count>=0;count--)
 {
    array[count]=(status%10)|0x30;
    status=(status/10);
 }

 display_alphanumeric(disp_cmd1);
 display_numeric(array);

 //program configuration register to change page size
 cs_low();

 mspi_xchange(0x3d);
 mspi_xchange(0x2a);
 mspi_xchange(0x80);
 mspi_xchange(0xa6);

 cs_high();

 while(1);
}

void init_mspi()
{
 PORTC_DIR|=PIN1_bm|PIN3_bm|PIN5_bm|PIN7_bm; //define XCK0, TXD0, SCK and MOSI as output pins
 PORTD_DIR|=PIN2_bm; //SS of dataflash as output
 PORTC_REMAP|=PORT_SPI_bm; //swap locations of MOSI & SCK pins
 PORTC_REMAP|=PORT_USART0_bm; //move pins from PORTC[3:0] to PORTC[7:4]
 PORTC_PIN5CTRL|=PORT_INVEN_bm; //enable INVEN on pin 5 for SPI mode 3

 USARTC0_CTRLC=0xc2; //MSPI mode selected, MSB out first, SPI mode 3 selected
 USARTC0_STATUS=USART_RXCIF_bm|USART_TXCIF_bm; //Clear transmit and receive complete interrupt flag
 USARTC0_CTRLB=USART_RXEN_bm|USART_TXEN_bm;
 USARTC0_BAUDCTRLA=0x03; //BAUD rate is 1MHz bps
 USARTC0_BAUDCTRLB=0x00; //keep BSCALE 0x00
}

unsigned char mspi_xchange(unsigned char tx_byte)
{
 unsigned char rx_byte;

 while(!(USARTC0_STATUS & USART_DREIF_bm));
 USARTC0_DATA=tx_byte;

 while(!(USARTC0_STATUS & USART_RXCIF_bm));
 rx_byte=USARTC0_DATA;

 return rx_byte;
}

void cs_low()
{
 PORTD_OUT&=~(PIN2_bm);
}

void cs_high()
{
 PORTD_OUT|=PIN2_bm;
}

void clock_init()
{
 OSC_PLLCTRL=OSC_PLLFAC3_bm; //select internal 2MHz oscillator as PLL clock source, PLL multiplication factor as 8
 OSC_CTRL=OSC_PLLEN_bm; //enable PLL

 while(!(OSC_STATUS & OSC_PLLRDY_bm)); //wait until PLL is locked to desired frequency and ready to use
 CCP=0xd8; //write Configuration Change Protection register

 CLK_CTRL=CLK_SCLKSEL2_bm; //select PLL as system clock source
 CCP=0xd8; //write Configuration Change Protection register
 CLK_PSCTRL=CLK_PSADIV0_bm; //select Prescaler A as 2, Prescaler B and Prescaler C as 1, Clksys=16MHz, Clkper4=Clkper2=Clkper=Clkcpu=8MHz
 CLK_RTCCTRL=CLK_RTCEN_bm; //enable RTC clock source as 1KHz from 32KHz ULP internal oscillator
}

void lcd_init()
{
 PORTE_DIR|=0x20; //define PORTE.5 as output pin
 PORTE_OUT|=0x20; //set PORTE.5 to turn on LCD backlight

 LCD_CTRLA|=LCD_ENABLE_bm|LCD_SEGON_bm; //enable LCD module and all segments of LCD
 LCD_CTRLB=LCD_PRESC_bm|LCD_CLKDIV1_bm|LCD_CLKDIV0_bm|LCD_LPWAV_bm; //prescaler 16, clock divider 4 and low power consumption.
 LCD_CTRLC=0x3f; //as all 40 pins are used as segment pins
 LCD_CTRLD=LCD_BLINKEN_bm|LCD_BLINKRATE1_bm; //enable LCD segment blinking feature
 LCD_CTRLE=0x08;                             //write value to blink colon segment
 LCD_CTRLF=0x1f; //i.e. 31 is the value of FCONT[5:0]
}

void display_alphanumeric(unsigned char *pattern)
{
 unsigned char direction,width;

 direction=direction_14seg;
 width=width_14seg;

 LCD_CTRLG=LCD_TDG1_bm|first_14seg;    //14 segments & 4 COM terminals, first_14seg is starting segment

 if(direction)
    direction=LCD_DEC_bm; //i.e. if set decrement, else increment

 for(;width!=0;width--)
 {
    if(*pattern=='\0')
       break;

    LCD_CTRLH=direction|(*pattern++);
 }
}

void display_numeric(unsigned char *pattern)
{
 unsigned char direction,width;

 direction=direction_7seg;
 width=width_7seg;

 LCD_CTRLG=LCD_TDG0_bm|first_7seg;    //7 segments & 4 COM terminals, first_7seg is starting segment

 if(direction)
    direction=LCD_DEC_bm; //i.e. if set decrement, else increment

 for(;width!=0;width--)
 {
    if(*pattern=='\0')
       break;

    LCD_CTRLH=direction|(*pattern++);
 }
}

Thanks for reading.




This post first appeared on Embedded Tutorials, please read the originial post: here

Share the post

XMEGA-B1 XPLAINED: USART in MASTER SPI MODE TUTORIAL

×

Subscribe to Embedded Tutorials

Get updates delivered right to your inbox!

Thank you for your subscription

×