Unreliable PEC on stm32 smbus - c

I've got a problem with PEC on STM32 SMBUS, which i use to read data from a MLX90614 IR thermometer. When i start the device, either the PECERR flag is set and continues to be set, for all transmissions, even though the data from the IR thermometer seems to be correct. Or the PECERR is never set and all the data from the IR thermometer is still correct.
When i study the data on my oscilloscope i can see no difference between the signals when the PECERR is set or not. As stated earlier data seems to be good either way.
I could of course just ignore the PECERR flag, but i'd lige to be able to filter out any eventual garbled transmissions. Anyone have an idea what i am doing wrong here?
void I2C_SMBUS_Initialize(I2C_TypeDef *h) {
h->CR2 &= ~I2C_CR2_FREQ; // Clear frequency part of register
h->CR2 |= 0x8; // Clock speed in Mhz
h->OAR1 = 0x4000;
h->CCR = 0;
h->CCR &= ~I2C_CCR_DUTY;
h->CCR |= 0x190;
h->TRISE &= ~I2C_TRISE_TRISE; // Clear TRISE bits
h->TRISE |= 0x9; // Set TRISE
h->CR1 |= I2C_CR1_ENPEC; // Enable packet error check
h->CR1 |= I2C_CR1_SMBTYPE; // SMBUS host
h->CR1 |= I2C_CR1_SMBUS; // SMBUS Mode
h->CR1 |= I2C_CR1_PE; // Start i2c
}
uint16_t I2C_SMBUS_ReadWord(I2C_TypeDef* h, uint8_t deviceAddress, uint8_t command) {
static const uint16_t ERROR_CODE = 0x3BFF;
//static const uint8_t deviceAddress = 0x5A;
static const uint8_t timeout = 100;
uint16_t temp = 0;
h->CR1 &= ~I2C_CR1_POS;
// Generate start bit
sendStartBit(h);
// Wait for start bit set
if (!waitFlag((&h->SR1), I2C_SR1_SB, BIT_SET, timeout)) {
DEBUG_PUTS("Timeout while waiting for start bit set");
return ERROR_CODE;
}
// Address byte. 7 bit. Shifted one lefet
sendAddress(h, deviceAddress, I2C_WRITE);
// Wait for address bit set
if (!waitFlag((&h->SR1), I2C_SR1_ADDR, BIT_SET, timeout)) {
DEBUG_PUTS("Timeout while waiting for address bit set");
return ERROR_CODE;
}
// Clear ADDR bit
clearAddressFlag(h);
sendData(h, command);
// wait for tx buffer empty
if (!waitFlag((&h->SR1), I2C_SR1_TXE, BIT_SET, timeout)) {
DEBUG_PUTS("Timeout while waiting for buffer empty");
return ERROR_CODE;
}
uint8_t length = 3;
uint8_t tmpBuffer[length+1];
memset(tmpBuffer, 0x00, 4);
// Enable automatic ACK generation
enableAutomaticACK(h);
// Generate start bit
sendStartBit(h);
// Wait for start bit set
if (!waitFlag((&h->SR1), I2C_SR1_SB, BIT_SET, timeout)) {
DEBUG_PUTS("Timeout while waiting for repeted start bit set");
return ERROR_CODE;
}
// Send the read command to the slave address
sendAddress(h, deviceAddress, I2C_READ);
// Wait for address bit set
if (!waitFlag((&h->SR1), I2C_SR1_ADDR, BIT_SET, timeout)) {
DEBUG_PUTS("Timeout while waiting for address bit set");
return ERROR_CODE;
}
// Clear ADDR bit by reading status register
clearAddressFlag(h);
// Now we must read the data from the slave
if (length > 2) {
// Receive the first n-2 bytes
for (uint8_t i=0; i < length-2; i++) {
// Wait for Byte Transfer ready
if (!waitFlag((&h->SR1), I2C_SR1_BTF, BIT_RESET, timeout)) {
DEBUG_PUTS("Timeout while waiting for Byte Transfer ready");
return ERROR_CODE;
}
tmpBuffer[i] = h->DR;
// Wait for Byte Transfer Finished
if (!waitFlag((&h->SR1), I2C_SR1_BTF, BIT_SET, timeout)) {
DEBUG_PUTS("Timeout while waiting for Byte Transfer Finished");
return ERROR_CODE;
}
}
// Wait for Byte Transfer ready
if (!waitFlag((&h->SR1), I2C_SR1_BTF, BIT_RESET, timeout)) {
DEBUG_PUTS("Timeout while waiting for Byte Transfer ready");
return ERROR_CODE;
}
// Disable automatic ACK generation
disableAutomaticACK(h);
// Read the second last byte
tmpBuffer[length-1] = h->DR;
// Send stop bit
sendStopBit(h);
// Enable packet error check
h->CR1 |= I2C_CR1_PEC;
// Read the last byte
tmpBuffer[length] = h->DR;
temp = tmpBuffer[3]*256 + tmpBuffer[2];
uint8_t pec = h->SR2 &= I2C_SR2_PEC_Msk;
if ((h->SR1 & I2C_SR1_PECERR) != 0) {
puts("PEC ERROR");
}
}
return temp;
}

Found the bug. PECERR must be cleared by software. The first transmission after power on occasionally fails.
if ((h->SR1 & I2C_SR1_PECERR) != 0) {
DEBUG_PUTS("PEC ERROR");
h->SR1 &= ~I2C_SR1_PECERR;
return ERROR_CODE;
}

Related

SPI Communication between STM32 and ADXL345

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

STM32F207 bare metal SPI RXNE and TXE not working?

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++) {
}
}

How to continuos transmit data?

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
}

No response from UART

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.

AVR Butterfly UART - can't receive data

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!

Resources