I am trying to use the SPI communication to read data from the ADXL345 accelerometer. I configured the different pins and SPI in master mode, and tried reading the x, y and z axis accelerations.
My issue is that the SPI readings are always 0. I tried debugging to find the issue and I realized that RXNE is never set even though I'm transmitting data and I don't really get why.
I'm using STM32F103 Board.
Here's my code:
#include "Driver_GPIO.h"
#include "stm32f10x.h"
uint8_t RxData[6];
int x,y,z;
float x_acc,y_acc,z_acc;
void GPIO_Config (void)
{
MyGPIO_Struct_TypeDef NSS={GPIOA,4,Out_OD}; // Output Open Drain
MyGPIO_Struct_TypeDef SCK={GPIOA,5,AltOut_Ppull}; // Alternate Output Push-Pull
MyGPIO_Struct_TypeDef MISO={GPIOA,6,In_Floating}; // Input Floating
MyGPIO_Struct_TypeDef MOSI={GPIOA,7,AltOut_Ppull}; // Alternate Output Push-Pull
RCC->APB2ENR |= RCC_APB2ENR_IOPAEN; //enable GPIOA clk
MyGPIO_Init(&NSS);
MyGPIO_Init(&SCK);
MyGPIO_Init(&MISO);
MyGPIO_Init(&MOSI);
}
void SPI_Enable (void)
{
SPI1->CR1 |= (SPI_CR1_SPE); // SPE=1, Peripheral enabled
}
void SPI_Disable (void)
{
SPI1->CR1 &= ~(SPI_CR1_SPE); // SPE=0, Peripheral Disabled
}
void CS_Enable (void)
{
GPIOA->BSRR |= GPIO_BSRR_BR9;
}
void CS_Disable (void)
{
GPIOA->BSRR |= GPIO_BSRR_BS9;
}
void SPI_Config(void){
RCC->APB2ENR |= RCC_APB2ENR_SPI1EN; // Enable SPI1 CLock
SPI1->CR1 |= SPI_CR1_CPOL| SPI_CR1_CPHA; // CPOL=1, CPHA=1
SPI1->CR1 |= SPI_CR1_MSTR; // Master Mode
SPI1->CR1 |= (SPI_CR1_BR_0)| (SPI_CR1_BR_1); // BR[2:0] = 400: fPCLK/16, PCLK2 = 72MHz, SPI clk = 3.375MHz
SPI1->CR1 &= ~SPI_CR1_LSBFIRST; // LSBFIRST = 0, MSB first
SPI1->CR1 |= (SPI_CR1_SSM) | (SPI_CR1_SSI); // SSM=1, SSI=1 -> Software Slave Management
SPI1->CR1 &= ~SPI_CR1_RXONLY; // RXONLY = 0, full-duplex
SPI1->CR1 &= ~SPI_CR1_DFF; // DFF=0, 8 bit data
SPI1->CR2 = 0;
}
void SPI_Transmission(uint8_t *data, int size){
uint8_t clear;
//check flag TxE //
int i=0;
while (i<size)
{
while (!((SPI1->SR)&(SPI_SR_TXE))){}; // buffer is empty
*(volatile uint8_t *)&SPI1->DR = data[i];
i++;
}
while (!((SPI1->SR)&(SPI_SR_TXE))){}; // buffer is empty
while (((SPI1->SR)&(SPI_SR_BSY))){}; // buffer not communicating
clear= SPI1->DR; // empty Overrun flag
clear= SPI1->SR;
}
void SPI_Receive (uint8_t *data,int size)
{
while (size)
{
while (((SPI1->SR)&(SPI_SR_BSY))) {}; // buffer not communicating
*(volatile uint8_t *)&SPI1->DR = 0; // dummy data
while (!((SPI1->SR) &(SPI_SR_RXNE))){};
// buffer is not empty
*data++= *(volatile uint8_t *)&SPI1->DR;
size--;
}
}
void adxl345_write (uint8_t address, uint8_t value)
{
uint8_t data[2];
data[0] = address|0x40; // multibyte write
data[1] = value;
CS_Enable (); // pull the cs pin low
SPI_Transmission (data,2); // write data to register
CS_Disable (); // pull the cs pin high
}
void adxl345_read (uint8_t address, uint8_t *RxData)
{
address |= 0x80; // read operation
address |= 0x40; // multibyte read
CS_Enable (); // pull the pin low
SPI_Transmission (&address,1); // send address
SPI_Receive (RxData,6); // receive 6 bytes data
CS_Disable ();; // pull the pin high
}
void adxl345_init (void)
{
adxl345_write (0x31, 0x01); // data_format range= +- 4g
adxl345_write (0x2d, 0x00); // reset all bits
adxl345_write (0x2d, 0x08); // power_cntl measure and wake up 8hz
}
int main(void)
{
GPIO_Config();
SPI_Config();
SPI_Enable();
adxl345_init();
do{
adxl345_read(0x32,RxData);
x = ((RxData[1]<<8)|RxData[0]); // DATA X0, X1
y = ((RxData[3]<<8)|RxData[2]); // DATA Y0, Y1
z = ((RxData[5]<<8)|RxData[4]); // DATA Z0, Z1
// Scale Factor for Xout, Yout and Zout is 7.8 mg/LSB for a +-4g, 10-bit resolution
// ==> multiply by 0.0078 to get real acceleration values
x_acc= x * 0.0078;
y_acc= y * 0.0078;
z_acc= z * 0.0078;
}while(1);
}
As already stated you have a lot of issues here
Why NSS pin is configured open-drain? Typically CS lines are push-pull. I don't know the schematics, but this is the first time I see an open-drain CS
In GPIO_Config NSS is pin 4, yet pin 9 is toggled from CS_Enable
If F1 series there is separate clocking bit for the alternate functions, it's not enabled
It is also weird that you are telling that RXNE is always zero and the readings returns zero. Your code should stuck in while loops if RXNE stays zero
I will not analyze magic numbers as I do not have time check every number in the RM. But you have many obvious issues.
Deley or readback is required after enabling the peripheral clock. You instantly set the registers which is wrong. Add __DSB(); or readback (for example (void)RCC->APB2ENR;). Same for all peripherals
I need to communicate with an eeprom chip(25lc1024) via SPI.
I can get the following code to work but have to resort to delays isntead of the flags alone.
Otherwise I can see on the scope that it is not waiting for all the bits to be shifted out and proceeds.
Any ideas why the flags are not stopping it?
void Init_SPI(void){
RCC->APB2ENR |= RCC_APB2ENR_SPI1EN; // enable SPI clock
GPIOA->AFR[0] |= SET_AFL_A_FOR_SPI1;
GPIOB->AFR[0] |= SET_AFL_B_FOR_SPI1;
SPI1_NSS = DESELECT_CHIP; // set the SS pin high
// initialize the SPI configuration register
SPI1->CR1 = SPI_CR1_MSTR // SPI master mode
| SPI_CR1_BR_1; // 010 bit rate prescale /8 (60MHz/8 = 7.5MHz)
SPI1->CR2 = SPI_CR2_SSOE;
SPI1->CR1 |= SPI_CR1_SPE; // enable SPI
}
unsigned int eeprom_read(unsigned long address, unsigned int chars_to_read)
{
char temp;
unsigned int result = 0;
SPI1_NSS = SELECT_CHIP; // set the SS pin low
SPI1->DR = READ_FROM_SPI; // send data out SPI
while( !(SPI1->SR & SPI_SR_TXE) ); // wait until transmit buffer empty
SPI1->DR = (address & 0xFF0000) >> ADJ16; // send data out SPI
while( !(SPI1->SR & SPI_SR_TXE) ); // wait until transmit buffer empty
SPI1->DR = (address & 0x00FF00) >> ADJ8; // send data out SPI
while( !(SPI1->SR & SPI_SR_TXE) ); // wait until transmit buffer empty
SPI1->DR = (address & 0x0000FF); // send data out SPI
while( !(SPI1->SR & SPI_SR_TXE) ); // wait until transmit buffer empty
delay();
temp = SPI1->DR;
SPI1->DR = DUMMY_8BIT; // send dummy 8 bits to generate clock
chars_to_read--;
while( !(SPI1->SR & SPI_SR_RXNE) ); // wait until receive buffer is not empty
//USART2->DR = SPI1->DR;
result = SPI1->DR;
if(chars_to_read != 0){
while((SPI1->SR & SPI_SR_RXNE) ); // wait until receive buffer is empty
delay();
SPI1->DR = DUMMY_8BIT; // send dummy 8 bits to generate clock
//delay();
while( !(SPI1->SR & SPI_SR_TXE) ); // wait until transmit buffer is empty
while( !(SPI1->SR & SPI_SR_RXNE) ); // wait until receive buffer is not empty
temp = SPI1->DR;
result = (result*256) + temp;
chars_to_read--;
}
delay();
SPI1_NSS = DESELECT_CHIP; // set the SS pin high
return result;
}
static void delay(void)
{
unsigned int j = FALSE;
for (j = 0; j < 25; j++) {
}
}
Chip:C8051F321 & PCF85176(LCD Driver)
I am trying to communicate from C8051F321(MASTER) to PCF85176(SLAVE), and the original code is only transmit for one byte like this:
START → SLAVE ADDR → DATA → ACK → STOP
But I tried to make it like
START → SLAVE ADDR → Command → ACK → DATA → ACK...→ ACK ..→ ACK .. →
STOP
And it always no effect like my code
How can I correct my code?
Thanks for helping~
void SMBus_ISR (void) interrupt 7
{
bit FAIL = 0; // Used by the ISR to flag failed
// transfers
static bit ADDR_SEND = 0; // Used by the ISR to flag failed
unsigned char a; // transmissions as slave addresses
if(ARBLOST == 0)
{
switch (SMB0CN & 0xF0) // Status vector
{
// Master Transmitter/Receiver: START condition transmitted.
case SMB_MTSTA:
SMB0DAT = TARGET; // Load address of the target slave
SMB0DAT &= 0xFE; // Clear the LSB of the address for the
// R/W bit
SMB0DAT |= SMB_RW; // Load R/W bit
STA = 0; // Manually clear START bit
ADDR_SEND = 1;
break;
// Master Transmitter: Data byte
case SMB_MTDB:
if (ACK) // Slave Address or Data Byte
{ // Acknowledged?
if (ADDR_SEND)
{
ADDR_SEND = 0; // Next byte is not a slave address
if(SMB_DATA_OUT==0xF0)
{
SMB0DAT = SMB_DATA_OUT;
ADDR_SEND = 0;
}
else
{
SMB0DAT = SMB_Command_OUT;
ADDR_SEND = 1;
}
}
else // If previous byte was not a slave address,
{
STO =1; // Set STO to terminate transfer
SMB_BUSY =0; // And free SMBus interface
}
}
else
{
STO = 1; // Set STO to terminate transfer
STA = 1; // By a START
NUM_ERRORS++; // Indicate error
}
break;
//Master Receiver:byte received
case SMB_MRDB:
SMB_DATA_IN =SMB0DAT; // Store received byte
SMB_BUSY = 0; // Free SMBus interface
ACK = 0; // Send NACK to indicate last byte
// of this transfer
STO = 1; // Send STOP to terminate transfer
break;
default:
FAIL = 1; // Indicate failed transfer
// and handle at end of ISR
break;
}//end switch
}
else
{
//ARBLOST = 1,error occurred...abort transmission
FAIL = 1;
} //end ARBLOST if
if(FAIL) // If the transfer failed,
{
SMB0CF &= ~0x80; // Reset communication
SMB0CF |= 0x80;
STA = 0;
STO = 0;
ACK = 0;
SMB_BUSY = 0; // Free SMBus
FAIL = 0;
NUM_ERRORS++; // Indicate an error occurred
}
SI = 0; //Clear interrupt flag
}
I'm able to receive with the following code, but unfortunately, nothing is sent back. What am I doing wrong?
#include <pic18f25k80.h>
#include "config.h"
#include <usart.h>
int i = 0;
unsigned char MessageBuffer[200];
void main() {
OSCCONbits.IRCF = 0b110; // 8MHz
TRISB6 = 0; // TX set as output
TRISB7 = 0; // RX set as output
// Clear TX interrupt
// Set RX interrupt
// 8-bit Asynch. mode
// BRGH = 1 = high baud mode
// 51 = ((8MHz/baud)/16)-1 with baud = 9600
Open2USART(USART_TX_INT_OFF & USART_RX_INT_ON & USART_ASYNCH_MODE
& USART_EIGHT_BIT & USART_BRGH_HIGH, 51 );
RC2IF = 0; // reset RX2 flag
RC2IP = 0; // not high priority
RC2IE = 1; // Eneble RX2 interrupt
INTCONbits.PEIE = 1; // enable peripheral interrupts
INTCONbits.GIE = 1; // enable interrupts
RCSTA2bits.SPEN = 1; // enable USART
while(1){
}
}
void interrupt ISR () {
if(PIR3bits.RC2IF == 1) {
if(i<200) { // buffer size
MessageBuffer[i] = Read2USART(); // read byte from RX reg
if (MessageBuffer[i] == 0x0D) { // check for return key
puts2USART(MessageBuffer);
for(;i>0;i--)
MessageBuffer[i] = 0x00; // clear array
i=0;
return;
}
i++;
RC2IF = 0; // clear RX flag
} else {
puts2USART(MessageBuffer);
for(;i>0;i--)
MessageBuffer[i] = 0x00; // clear array
i = 0;
return;
}
}
}
I'm transmitting the 0x41 hex code, I checked with the scope and see that is is being received. And according to the code I have, an echo of the received data should be sent back. When I check the TX pin, nothing is happening.
Add USART_CONT_RX to Open2USART to enable continuous receive.
Also, it's a good idea to do the minimum necessary in the interrupt service routine. Consider something like:
void interrupt ISR () {
char data;
if(PIR3bits.RC2IF == 1) {
data = Read2USART(); // always read byte from RX reg (clears RC2IF)
if(i<200) { // buffer size
MessageBuffer[i] = data; // read byte from RX reg
i++;
}
else{
// flag buffer full error
}
}
}
and doing the rest of what you are doing in the while(1) loop.
I am using the UART of Atmega169/AVR Butterfly for transmission to another board, baudrate 56700, no parity, 1 stopbit, no flow control. The oscillator is running at 7,3768Mhz (checked). I can transmit data successfully (checked with the other board and PC/HyperTerminal), but not receive any data - when running the debugger the configuration bits are all set correctly, but RXC is false constantly - I also checked if I can send data to myself (connected TXD to RXD and grounded), but without success. (Tried with ISR as well as polling)
Below are the relevant parts of the code, I hope you can deal with it - PORTB is used as output for testing with the oscilloscope (I know I could just use one pin, but there is nothing else on PORTB right now):
int main(void){
OSCCAL_Calibrate(); // calibrate the internal oscillator
int UBRR_VAL = ((F_CPU)/(BAUD*16)-1);
UART_Init(UBRR_VAL);
DDRB |= 0xFF;
PORTB = 0;
testCharSend();
while(1);
return 0;
}
void testCharSend()
{
char i = 'x';
while(1){
Uart_Tx(i);
}
}
void UART_Init(unsigned int baudrate)
{
// Set baud rate
UBRRH = (unsigned char)(baudrate>>8);
UBRRL = (unsigned char)baudrate;
UCSRA = 0;
// Enable receiver and transmitter
UCSRB = (1<<RXEN)|(1<<TXEN);
// Async. mode, 8bit, No parity, 1 stop bit (like CC2540)
UCSRC = (0<<UMSEL)|(0<<UPM0)|(0<<USBS)|(3<<UCSZ0)|(0<<UCPOL);
// enable receive interrupt
UCSRB |= (1<<RXCIE);
// flush UART
UART_Flush();
}
void UART_Flush( void )
{
unsigned char dummy;
while ( UCSRA & (1<<RXC) ) dummy = UDR;
}
void Uart_Tx(char data)
{
while (!(UCSRA & (1<<UDRE)));
UDR = data;
}
ISR (USART0_RX_vect)
{
PORTB ^= 0xFF;
char c = UDR;
}
OK, I tested the connections with an oscilloscope, the RXD line on the board was broken, switched the board and now it's working, so the code above is valid!