SPI: SPI: TXE is not cleared when I transmit data - c

void SPI_SendData(SPI_RegDef_t *pSPIx ,uint8_t *pTxBuffer,uint32_t len)
{
while(len > 0)
{
// 1. chờ cờ TXE set (chờ cho thanh txbuffer trống)
while (SPI_GetFlagStatus(pSPIx, SPI_FLAG_TXE) == FLAG_RESET);
//2. kiểm tra DFF
if(((pSPIx->CR1 >> SPI_CR1_DFF) & 1) == SPI_DFF_16BIT)
{
// 16 bit
pSPIx->DR = *((uint16_t*) pTxBuffer);
(uint16_t*) pTxBuffer++;
len--;
}else
{
pSPIx->DR = *pTxBuffer;
pTxBuffer++;
}
len--;
}
}
I think, Clearing the TXE bit is performed by writing to the SPI_DR register. The TXE Flag is not cleared but the RXEN flag is turned on when I write data to the SPI_DR register.
Why is the RXEN flag is turned on?
why is the TXE Flag not cleared?

(uint16_t*) pTxBuffer++; this is wrong
pSPIx->DR = *pTxBuffer; it is wrong as well as it transfers 32 bits instead of 8. If your micro has SPI FIFO - you will send 4 bytes.
Clearing the TXE bit is performed by writing to the SPI_DR register
Yes but when data was moved to the shift register this flag is set indicating that you can put more data. So it will be cleared for a very short time.
Why is the RXEN flag is turned on?
You probably mean RXNE. It is set as master received data at the same time when it transmits (BTW it is the only way to receive anything - you need to transmit dummy data). It is 100% correct behaviour

Related

Getting an ACK failure right away after writing slave address in interrupt mode [STM32F401RE]

I see that once slave address is written in the interrupt handler, I get an ACK Failure right away, but when I do the same thing with polling approach, I do get an ACK and goes on to read off the values from the device.
With interrupts, I see ISR gets triggered only once (in which it writes slave address) and never gets called again. My i2c configurations are fine, but there's something with interrupts that I seem to be missing out on.
Polling approach
HAL_Status I2C_HAL_TX_IT () {
GenerateStartCondition();
// validate the completion of start condition
while (!GetFlagStatus(hi2c->Instance, I2C_SR1_SB) && HAL_Timeout(5));
// write slave address along with write bit
I2C_WriteSlaveAddress(hi2c, WRITE);
// wait for address to be sent
while (!GetFlagStatus(hi2c->Instance, I2C_SR1_ADDR) && HAL_Timeout(5));
// clear address flag
I2C_ClearADDRFlag(hi2c->Instance);
// write data to DR ...
}
With interrupts:
void HAL_StartI2CInterrupts() {
GenerateStartCondition();
// setting control bits
hi2c->Instance->CR2 |= I2C_CR2_ITBUFEN;
hi2c->Instance->CR2 |= I2C_CR2_ITEVTEN;
hi2c->Instance->CR2 |= I2C_CR2_ITERREN;
}
void I2C1_EV_IRQHandler ()
{
uint8_t event_interrupt = (hi2c->Instance->CR2 & I2C_CR2_ITEVTEN) >> I2C_CR2_ITEVTEN_Pos;
uint8_t buffer_interrupt = (hi2c->Instance->CR2 & I2C_CR2_ITBUFEN) >> I2C_CR2_ITBUFEN_Pos;
uint8_t var;
if (event_interrupt)
{
// validate the completion of START condition
var = (hi2c->Instance->SR1 & I2C_SR1_SB) >> I2C_SR1_SB_Pos;
if (var)
{
if (hi2c->I2C_State == I2C_TX_BUSY)
{
I2C_WriteSlaveAddress(hi2c, WRITE);
}
}
// check ADDR bit ...
}
}

I2C ACKEN Bit doesnt get cleared by hardware

I am using a PIC16F1789 and an MPU-9250. The Code inside my I2C Reading funciton looks like this:
unsigned char i2cSensor_Read(unsigned char regAddr){
unsigned char val;
// Start
i2cWait();
SEN = 1;
// Address + Write Bit
i2cWait();
SSP1BUF = ((slvAdd<<1) | (0b0<<0)); // address slave + write (0)
i2cWait();
//Register address
SSP1BUF = regAddr; // address register
i2cWait();
//Start
RSEN = 1;
i2cWait();
// Address + Read Bit
SSP1BUF = ((slvAdd<<1) | (0b1<<0)); //Address + read (1)
i2cWait();
// Read data
RCEN = 1;
i2cWait();
val = SSP1BUF;
ACKDT = 1; // set acknowlege Bit (1 = Not Acknowlege, 0 = Acknowlege)
ACKEN = 1; // send acknowlege Bit
// Stop
i2cWait();
PEN = 1;
return val;
}
When calling the last i2cWait(), the program hangs.
The wait function looks like this:
void i2cWait(){
while((SSP1STAT & 0x04) || (SSP1CON2 & 0x1F));
}
I've worked with the "Single-Byte Read Sequence" on page 35 of the 9250 Datasheet: https://cdn.sparkfun.com/assets/learn_tutorials/5/5/0/MPU9250REV1.0.pdf
And the PIC Datasheet: http://ww1.microchip.com/downloads/en/DeviceDoc/40001675C.pdf
When debugging, the program gets stuck in the i2cWait() after I send the NACK Bit. It gets stuck because the ACKEN Bit (Bit 4) of the SSPCON2 register (Page 341 of PIC datasheet) doesn't get cleared, so the program gets stuck in the while().
Why is this the case? Does the Slave have to clear this bit? Is the slave device broken?
I think the issue is really the BF flag is still set and not allowing the ack to be clocked out. As soon as you set RCEN, the BF flag is cleared, thus the call to wait does nothing and val probably contains old data, not new. After 8 clocks, the BF flag is then set and SSP1BUF has the new data. Again, the issue is that BF is set because you didn't read the data buffer after receiving the data, thus the baud rate generator (clock source) is suspended:
"the contents of the SSPSR are loaded into the SSPBUF, the BF flag bit
is set, the SSP1IF flag bit is set and the Baud Rate Generator is
suspended from counting, holding SCL low. The MSSP is now in Idle
state awaiting the next command. When the buffer is read by the CPU,
the BF flag bit is automatically cleared. The user can then send an
Acknowledge bit at the end of reception by setting the Acknowledge
Sequence Enable, ACKEN bit of the SSPCON2 register."
To fix this, you should poll the BF flag looking for a true after you write RCEN.
There are other issues, such as the wait after writing to SEN probably does nothing either, since writing to SEN only SSP1IF bit (SSP1IF is set by hardware on completion of the Start. )

Using Interrupt to Transmit via USART on AVR MCU

I believe I understand how to use interrupts to receive serial data on UART of an ATmega328p, but I don't understand the mechanics of how to transmit data.
Here is a basic program that I want to use to transmit the character string "hello" using interrupts to drive transmission. I understand that the character 'o' will likely be transmitted twice, and I am ok with that.
#include <avr/io.h>
#include <avr/interrupt.h>
#define F_CPU 16000000UL
#define BAUD 19200
#define DOUBLE_SPEED 1
void initUART(unsigned int baud, unsigned int speed);
volatile uint8_t charIndex = 0;
volatile unsigned char command[5] = "hello";
int main(void)
{
//initialize UART
initUART(BAUD, DOUBLE_SPEED);
sei();
//What do I put here to initiate transmission of character string command?
//Is this even correct?
UDR0 = command[0];
while(1)
{
}
}
ISR(USART_TX_vect)
{
// Transmit complete interrupt triggered
if (charIndex >= 4)
{
//Reach the end of command, end transmission
return;
}
//transmit the first char or byte
UDR0 = command[charIndex];
//Step to the next place of the command
charIndex++;
}
void initUART(unsigned int baud, unsigned int speed)
{
unsigned int ubrr;
if(speed)
{
//double rate mode
ubrr = F_CPU/8/baud-1;
//set double speed mode
UCSR0A = (speed << U2X0);
}
else
{
//normal rate mode
ubrr = F_CPU/16/baud-1;
}
//set the baud rate
UBRR0H = (unsigned char)(ubrr >> 8);
UBRR0L = (unsigned char)(ubrr);
//enable Tx and Rx pins on MCU
UCSR0B = (1 << RXEN0) | (1 << TXEN0);
//enable transmit interrupt
UCSR0B = (1 << TXCIE0);
//set control bits, 8 bit char, 0 stop, no parity
UCSR0C = (1 <<UCSZ00) | (1 <<UCSZ01);
}
My understanding is that if I wrote the first character to UDR0 (as I did in main()), this would then trigger a Transmit Complete Interrupt, and then the next byte would be transmitted via the ISR. This does not seem to work.
The code shown here compiles using gcc. Can someone offer an explanation?
The key thing to understand is that the USART has 2 separate hardware registers that are used in the data transmission: UDRn and the Transmit Shift Register, which I'll just call TSR from now on.
When you write data to UDRn, assuming no tx is in progress, it'll get moved to the TSR immediately and the UDRE irq fires to tell you that the UDRn register is "empty". Note that at this point the transmission has just started, but the point is that you can already write the next byte to UDRn.
When the byte has been fully transmitted, the next byte is moved from UDRn to TSR and UDRE fires again. So, you can write the next byte to UDRn and so on.
You must only write data to the UDRn when it is "empty", otherwise you'll overwrite the byte it's currently storing and pending transmission.
In practice, you don't usually mind about the TXC irq, you want to work with the UDRE to feed more data to the USART module.
The TXC irq, however, is useful if you need to perform some operation when the transmission has actually completed. A common example when dealing with RS485 is to disable the transmitter once you're done sending data and possibly re-enable the receiver that you could have disabled to avoid echo.
Regarding your code
Your main issue is that you're setting UCSR0B 2 times in initUART() and the second write clears the bits you just set, so it's disabling the transmitter. You want to set all bits in one go, or use a |= on the second statement.

I2C Read function gets stuck after sending Acknowledge bit

I'm trying to communicate with a MPU-9250 (Accelerometer and a lot of other stuff) from my PIC16F1789. My functions look like the following:
void i2cWait(){
while((SSP1STAT & 0x04) || (SSP1CON2 & 0x1F));
}
unsigned char i2cReadCycle(unsigned char regAddr){
unsigned char val;
// Start
i2cWait();
SEN = 1;
// Address + Write Bit
i2cWait();
SSPBUF = (slvAdd<<1 | (0b1<<0)); // address slave + write
i2cWait();
//Register address
SSP1BUF = regAddr; // address register + read
i2cWait();
//Start
SEN = 1;
i2cWait();
// Address + Read Bit
SSP1BUF = ((slvAdd<<1) | (0b0<<0)); //Address + read (0)
i2cWait();
// Daten Auslesen
RCEN = 1;
i2cWait();
val = SSP1BUF;
i2cWait();
ACKDT = 1; // set acknowledge Bit (1 = Not Acknowledge, 0 = Acknowledge)
ACKEN = 1; // send acknowledge Bit
// Stop
i2cWait();
PEN = 1;
return val;
}
I've worked with the "Single-Byte Read Sequence" on page 35 of the 9250 Datasheet:
https://cdn.sparkfun.com/assets/learn_tutorials/5/5/0/MPU9250REV1.0.pdf
And the PIC Datasheet:
http://ww1.microchip.com/downloads/en/DeviceDoc/40001675C.pdf
When debugging, the program gets stuck in the i2cWait() after I send the NACK Bit.
It gets stuck because the ACKEN Bit (Bit 4) of the SSPCON2 register (Page 341 of PIC datasheet) doesn't get cleared, so the program gets stuck in the while().
Why doesnt the Bit get cleared by hardware?
It looks like you are using 0b1<<0 for "writing" together with the slave address. However, according to the datasheet of MPU9250REV1.0, it should be 0. Check section 7.4 on page 35.
Although it might be counter-intuitive to set '0' for writes, it makes sense if you think of the "General Call" that ist initiated with the slave address 0b0000000 and the "write" bit 0.
This also means to change the "read" bit in your code to 1.
In the current implementation, mixing up read and write bits, leads to the issue being stuck waiting after setting the NACK.

Problems with PIC A/D conversion

I am trying to read analogic signal for a sort of mouse with a pic18f14k50 controller. Here the simple circuit: http://dl.dropbox.com/u/14663091/schematiconew.pdf . I have to read analogic signal from AN9 circuit port. Main function reads from the port, and blinks 30 time if threshold is reached:
void main(void) {
InitializeSystem();
#if defined(USB_INTERRUPT)
USBDeviceAttach();
#endif
while(1) {
if((USBDeviceState < CONFIGURED_STATE)||(USBSuspendControl==1)) continue;
if(!HIDTxHandleBusy(lastTransmission))
{
int readed = myReadADC2(); //Here i tried both myReadADC2() or myReadADC1()
if(readed>40) { //If read threshold > 40, blink led 30 times
int i;
for(i=0; i<30; i++) {
Delay1KTCYx(0);
mLED_1_On();
Delay1KTCYx(0);
mLED_1_Off();
}
}
lastTransmission = HIDTxPacket(HID_EP, (BYTE*)hid_report_in, 0x03);
}//end while
}//end main
I used two method to read from the AN9 port, myReadADC() that uses OpenADC() API method:
int myReadADC(void) {
#define ADC_REF_VDD_VDD_X 0b11110011
OpenADC(ADC_FOSC_RC & ADC_RIGHT_JUST & ADC_12_TAD, ADC_CH9 & ADC_INT_OFF, ADC_REF_VDD_VDD_X & ADC_REF_VDD_VSS, 0b00000010); // channel 9
SetChanADC(ADC_CH9);
ConvertADC(); // Start conversion
while(BusyADC()); // Wait for completion
return ReadADC(); // Read result
}
and myReadADC2(), that implements manual read from the port.
int myReadADC2() {
int iRet;
OSCCON=0x70; // Select 16 MHz internal clock
ANSEL = 0b00000010; // Set PORT AN9 to analog input
ANSELH = 0; // Set other PORTS as Digital I/O
/* Init ADC */
ADCON0=0b00100101; // ADC port channel 9 (AN9), Enable ADC
ADCON1=0b00000000; // Use Internal Voltage Reference (Vdd and Vss)
ADCON2=0b10101011; // Right justify result, 12 TAD, Select the FRC for 16 MHz
iRet=100;
ADCON0bits.GO=1;
while (ADCON0bits.GO); // Wait conversion done
iRet=ADRESL; // Get the 8 bit LSB result
iRet += (ADRESH << 8); // Get the 2 bit MSB result
return iDelay;
}
Both cases doesn't works, i touch (sending analogic signal) port AN9 but when I set high threshold (~50) led don't blinks, with low threshold (~0) it blinks immidiatly when i provide power to the PIC. Maybe i'm using wrong port? I'm actually passing AN9 as reading port? Or maybe threshold is wrong? How can i found the right value? Thank you
Here the MPLAB C18 Apis http://dl.dropbox.com/u/14663091/API%20microchip%20C18.pdf .
Regarding function myReadADC2(): you need to switch ANSEL and ANSELH configs as RC7/AN9 is configured in bit 1 of ANSELH. Also call me paranoid but for the line
iRet += (ADRESH << 8);
I always like to either save it a temporary variable first or cast explicitly the value ADRESH before shifting it up:
iRet += (((UINT) ADRESH) << 8);
That way I know for sure the bits won't get lost when shifting up which has bitten me before.
Regarding function myReadADC():
OpenADC() only takes two parameters. I presume that bitfield in the third parameter field is for the analog enable (ADRESH/ADRES). I'm assuming that's handled by SetChanADC() but you may have to set ADRESH/ADRES manually. It may help to set a breakpoint in the debugger and stop after configuration is complete to make sure your registers are set appropriatley.

Resources