I have RX interrupts working just fine, but I wanted to add TX interrupts. I respond to long commands over the UART and don't want to waste cycles waiting for the TX to complete before sending along the next byte. I am trying to enable interrupts, transmit the data that needs to be transmitted, then disable the interrupts until the next TX packet comes along.
This works fine for the FIRST payload that I send out. I see it go out just fine. However as soon as I disable TX Interrupts once, I am never able to enter the ISR again. How on the MSP430 does one enable TX Interrupts on the UART and have it ever get into the ISR again?
Below where you see EUSCI_A_UART_enableInterrupt call, shouldn't this line trigger my ISR every time the interrupt gets enabled? If not, HOW DO I GET BACK INTO THE ISR AGAIN?
Here's the Transmit code:
void UartSendChar(uint8_t tx_char)
{
ring_buffer_put(_rbdTx, &tx_char);
EUSCI_A_UART_enableInterrupt(EUSCI_A1_BASE, EUSCI_A_UART_TRANSMIT_INTERRUPT); // Enable interrupt
}
Here's my ISR:
void EUSCI_A1_ISR(void)
{
int c=-1;
switch(__even_in_range(UCA1IV,USCI_UART_UCTXCPTIFG))
{
case USCI_NONE: break;
case USCI_UART_UCRXIFG:
...
case USCI_UART_UCTXIFG:
// If there's something in the Ring Buffer, transmit it
// If not, then disable TX interrupts until new data gets written.
if (ring_buffer_get(_rbdTx, &c) == 0)
{
EUSCI_A_UART_transmitData(EUSCI_A1_BASE, (uint8_t)c);
}
else
{
EUSCI_A_UART_disableInterrupt(EUSCI_A1_BASE, EUSCI_A_UART_TRANSMIT_INTERRUPT);
}
I figured out the problem. In this particular MSP430, when the interrupt vector is read and it shows a TX interrupt, the TXIFG bit automatically gets cleared by the micro. (How nice of it)
In order to get this ISR to fire again after re-enabling TX interrupts, the TXIFG bit must be manually set back to 1 again, which shows an interrupt pending. That way, when interrupts are enabled after data is shoved into the queue, the TXIFG bit is set, so the ISR executes and ships the data off.
Now my ISR looks like this:
void EUSCI_A1_ISR(void)
{
int c=-1;
switch(__even_in_range(UCA1IV,USCI_UART_UCTXCPTIFG))
{
case USCI_NONE: break;
case USCI_UART_UCRXIFG:
...
case USCI_UART_UCTXIFG:
// If there's something in the Ring Buffer, transmit it
// If not, then disable TX interrupts until new data gets written.
if (ring_buffer_get(_rbdTx, &c) == 0)
{
EUSCI_A_UART_transmitData(EUSCI_A1_BASE, (uint8_t)c);
}
else
{
EUSCI_A_UART_disableInterrupt(EUSCI_A1_BASE, EUSCI_A_UART_TRANSMIT_INTERRUPT);
// Set TXIFG manually back to 1 for next time.
HWREG16(EUSCI_A1_BASE + OFS_UCAxIFG) |= EUSCI_A_UART_TRANSMIT_INTERRUPT;
}
Related
I am stuck on the following problem. Consider this code:
int main(void)
{
SysTickInit();
USART_GPIOInits();
USART_Inits();
char data[] = "hello\n";
for(uint8_t i=0; i<10; i++)
{
HAL_UART_Transmit(&Usart1, (uint8_t*)data, strlen(data), 1000);
}
while(1){}
}
I try to send hello\n to Hercules in 10 times, but Hercules did not receive what i sent
this is what Hercules got , it had þ every the first time I reset the MCU. But , when I used Debugger mode, it did not get any error.
below is transmit function
below is Init function
but want to communicate with fingerprint , but because of this wrong i cant communicate
Change the order of initialization.
From
USART_GPIOInits();
USART_Inits();
to
USART_Inits();
USART_GPIOInits();
UART's default line state is logic high, logic low (start bit) launches a new transfer.
When GPIO is inialized first, with the corresponding peripheral module disabled, most likely you'll gen a logic low level on the TX pin, because there is no one to set it to a logic high (since UART is still disabled). When UART is initialized, it sets the TX line to a logic high (stop bit), and the terminal appication receives it as a broken byte.
Check your schematics
During and after reset CPU outputs are tri-stated. Most likely they'll stay at zero level until the configuration code will do it's job, leading to the same issue - receiving a garbage byte after the reset.
To prevent it, voltage levels on the interface pins must be defined during reset phase with an external pull-up resistor, like 10kOm, from TX and RX pins to VCC.
I'm facing a weird issue. I've always used bit bangin I2C functions on my PIC16F1459, but now I want to use the MSSP (SPI,I2C Master Slave Peripheral). So I've started writing the functions according to the datasheet, Start, Stop, etc. The problem I have is my PIC won't ACK the data I send to the I2C EEPROM. It clearly says in the datasheet that the ACK status can be found at SSPCON2.ACKSTAT. So my guess was to poll this bit until the slave responds to my data, but the program hangs in the while Loop.
void vReadACK (void)
{
while (SSPCON2.ACKSTAT != 0);
}
And here's my write function, my I2CCheck function and I2C Master Initialization function
void vI2CEcrireOctet (UC ucData, UC ucRW)
{
vI2CCheck();
switch (ucRW)
{
case READ:
SSPBUF = ucData + 1;
break;
case WRITE:
SSPBUF = ucData + 0;
break;
}
vReadACK();
}
void vI2CCheck (void)
{
while (SSPCON2.ACKEN); //ACKEN not cleared, wait
while (SSPCON2.RCEN); //RCEN not cleared, wait
while (SSPCON2.PEN); //STOP not cleared, wait
while (SSPCON2.SEN); //Start not cleared, wait
while (SSPCON2.RSEN); //Rep start not cleared, wait
while (SSP1STAT.R_NOT_W); //TX not done wait
}
void vInitI2CMaster (void)
{
TRISB4_bit = 1; //SDA IN
TRISB6_bit = 1; //SCL IN
SSP1STAT.SMP = 1; //No slew rate
SSP1STAT.CKE = 0; //Disable SMBus inputs
SSPADD = 0x27; //100 KHz
SSPCON1 = 0b00101000; //I2C Master mode
SSPCON3 = 0b00000000; //Rien de slave
}
Just so you know, 24LC32A WriteProtect tied to VSS, A2-A1-A0 tied to GND, so adress 0xA0. 4k7 pull-ups are on I2C line. PIC16F1459 at 16MHz INTOSC.
I'm completely stuck. I've went through the MSSP datasheet 5 to 6 times without finding any issue. Can you guys help?
And here's my logic analyzer preview (removing the while inside vReadAck() )
Well it looks like I've found the answer to my question. What I was doing was the exact way of doing this. The problem seemed to be the Bus Free Time delay required for the slave to respond. At 16Mhz, my I2C was probably too fast for the EEPROM memory. So I've added a small Delay function right after the stop operation, so the write sequences are delayed and BAM, worked.
Bus free time: Time the bus
must be free before a new
transmission can start.
Despite the fact you "totally know" know "PIC won't ACK the data I send to the I2C EEPROM" because it's not supposed to, you still seem to misunderstand how I2C acknowledgements are supposed to work. They're called acknowledgements because they can be both positively (ACK) and negatively (NAK) acknowledged. If you look at the the analyzer screen shot you posted you'll find that its quite clearly labelled each byte being sent as having been NAK'ed by the transmitter.
To properly check for I2C ACKs you should be polling the trailing edge of the ACKTIM bit, and then checking the ACKSTAT bit to find out whether the slave transmitted an ACK or a NAK bit. Something like this:
int
vReadACK() {
while(!SSPCON3.ACKTIM);
while(SSPCON3.ACKTIM);
return SSPCON2.ACKSTAT;
}
As for why your slaved device is apparently NAKing each byte it isn't clear from the code you've posted, but there's a couple of notable omissions from your code. You need to generate start and stop conditions but you've shown no code to do this.
I have been looking all of for an answer to this problem and have not been able to find anything. When my ISR is triggered, it goes through and does everything that it is supposed to perfectly fine, then before exiting and returning back to the main loop the ISR executes again. Once it has gone though the second time it then returns back to the main loop. This only happens when I use a 115V relay to operate the interrupt.
I am trying to detect when there is a power outage or when the power comes back on. I am using a Pin change interrupt to sense if the relay is closed or open. When the power goes out the relay will open and will trigger the ISR. If I connect this setup to a normal push button or switch everything works as needed and there is no problem, it is only when it is connected to the relay that there is a problem.
Here is the code that I have:(I know I don't need cli I have just been trying everything)
ISR(PCINT2_vect){
cli();
sbi(PORTC,5);
_delay_ms(6000);
cbi(PORTC,5);
for(delay_counter=0;delay_counter<2;delay_counter++)
{
_delay_ms(6000);
}
sbi(PORTC,5);
_delay_ms(6000);
if(bit_is_set(PIND,2))
{
lcd_clrscr();
lcd_puts("Sending SMS");
usart_print("at");
USART_Transmit('\r');
_delay_ms(6000);
for(i=0;i<=1;i++)
{
usart_print("at*smsm2m=");
USART_Transmit('"');
for(j=0;j<11;j++)
{
USART_Transmit(Alert_Numbers[i][j]);
}
usart_print(" Power has been lost");
USART_Transmit('"');
USART_Transmit('\r');
_delay_ms(6000);
}
lcd_clrscr();
lcd_puts("SMS Sent");
_delay_ms(6000);
lcd_clrscr();
lcd_puts("Status:NO POWER");
cbi(PORTC,5);
}
else if(bit_is_clear(PIND,2))
{
lcd_clrscr();
lcd_puts("System Reset");
_delay_ms(6000);
_delay_ms(6000);
usart_print("at");
USART_Transmit('\r');
_delay_ms(6000);
for(i=0;i<=1;i++)
{
usart_print("at*smsm2m=");
USART_Transmit('"');
for(j=0;j<11;j++)
{
USART_Transmit(Alert_Numbers[i][j]);
}
usart_print(" Pump regained power");
USART_Transmit('"');
USART_Transmit('\r');
_delay_ms(6000);
}
lcd_clrscr();
lcd_puts("POWER ON");
_delay_ms(6000);
lcd_clrscr();
lcd_puts("Status: Good");
}
else
{
}
}
int main(void)
{ /*Initializations*/
DDRC = 0x20; // PORTC,5 is now output
sbi(PORTC,5);
USART_Init(51);
lcd_init(LCD_DISP_ON);
lcd_clrscr();
/*Set interrupts*/
DDRD = 0b11111011; // set PD2 to input
PORTD = 0b00000100; // set PD2 to high
PCICR |= (1 << PCIE0);
PCMSK0 |= (1 << PCINT0);
PCICR |= (1<<PCIE2);
PCMSK2 |= (1<<PCINT18);
sei();
lcd_clrscr();
lcd_puts("Status: Good");
/*Main Program Loop: NOP*/
while(1)
{
lcd_clrscr();
lcd_puts("MAIN LOOP");
for(delay_counter=0;delay_counter<3;delay_counter++)
{
_delay_ms(6000);
}
}
}
I am using a Pin change interrupt to sense if the relay is closed or
open.
Don't do that. Seriously. Do not try to hook mechanical switches to an interrupt pin to trigger ISRs.
If you insist on doing so, at least make sure the switch's signal is decently debounced in hardware before it hits the µC's input pin.
Besides, waiting of any kind (_delay_ms(6000);) is not something one wants to have in an ISR.
The reason your code fails: When the signal is bouncing, even if you wait 6 seconds in your interrupt, the flag is set again (while you are still in your ISR). The quick and dirty solution would be to reset the flag just before you exit the ISR.
Two (in my opinion) better solutions:
If you want to do this in software, just set a variable in the ISR routine which indicates a event. Then, in your main loop (or in a function), do the debouncing of the relais (10 to 100ms should be enough).
if you want to do this in hardware, just add a R C combination as a filter to the pin (the c to ground and the r to the relais. Just try some combinations like 100k and 10µF.
My personal recommendation:
do both ;)
Annotations:
- using a 6 second delay in an interrupt routine is really a bad practice. Better: Use a Timer which is called every say 10 ms and increase a counter if the flag is set and the relais indicate power loss. If the relais has power, reset the flag and counter to 0. If the counter reaches 600, this means the power outage is longer than 6 seconds.
Have fun!
I had the problem myself, so I decided to answer it even though it is an old entry:
The reason why the interrupt fires twice is that the interrupt flag is not reset. This is a problem of some atmega types. You can solve the problem by one simple line:
Post this at the very end of you ISR interrupt function:
PCIFR |= (1<<PCIF2);
This will write a "1" into the interrupt-flag bit for the PCINT2 interrupt. If you are using other Interrupts, you have to set other flags to 1. Notice that the interrupt flag is inverted. So a 1 disables the interrupt, whereas a 0 triggers the interrupt.
Have a look into the datasheet:
http://www.atmel.com/images/doc2545.pdf
See Point 13.2.5:
Bit 2 - PCIF2: Pin change interrupt flag 2 -
When a logic change on any PCINT23..16 pin triggers an interrupt request, PCIF2 becomes set
(one). If the I-bit in
SREG and the PCIE2 bit in PCICR are
set (one), the MCU will jump to the
corresponding Interrupt Vector. The flag is cleared
when the interrupt routine is executed. Alternatively, the flag can be cleared by writing a logical one to it.
I hope this helped you and maybe some other people who have the same problem. Just search the datasheet for the name of the register and the bit corresponding to the interrupt flag and set it to 1.
Bye
I am transmitting data from my PIC24H microcontroller over 460Kbaud UART to a bluetooth radio module. Under most conditions, this flow works just fine and the bluetooth module uses CTS and RTS lines to manage flow control when its internal data buffers are full. However, there is a bug of some kind in the bluetooth module that resets it when data is continuously sent to it without any breaks, which happens if my data gets backed up in another bottleneck.
It would be nice if the module worked properly, but that's out of my control. So it seems that my only option is to do some data throttling on my end to make sure I don't exceed the data throughput limits (which I know roughly by experimentation).
My question is how to implement data rate throttling?
My current UART implementation is a RAM circular FIFO buffer 1024 bytes long that the main loop writes data to. A peripheral interrupt is triggered by the PIC when the last byte has been sent out by the UART hardware and my ISR reads the next byte from the buffer and sends it to the UART hardware.
Here's an idea of the source code:
uart_isr.c
//*************** Interrupt Service routine for UART2 Transmission
void __attribute__ ((interrupt,no_auto_psv)) _U2TXInterrupt(void)
{
//the UART2 Tx Buffer is empty (!UART_TX_BUF_FULL()), fill it
//Only if data exists in data buffer (!isTxBufEmpty())
while(!isTxBufEmpty()&& !UART_TX_BUF_FULL()) {
if(BT_CONNECTED)
{ //Transmit next byte of data
U2TXREG = 0xFF & (unsigned int)txbuf[txReadPtr];
txReadPtr = (txReadPtr + 1) % TX_BUFFER_SIZE;
}else{
break;
}
}
IFS1bits.U2TXIF = 0;
}
uart_methods.c
//return false if buffer overrun
BOOL writeStrUART(WORD length, BYTE* writePtr)
{
BOOL overrun = TRUE;
while(length)
{
txbuf[txWritePtr] = *(writePtr);
//increment writePtr
txWritePtr = (txWritePtr + 1) % TX_BUFFER_SIZE;
if(txWritePtr == txReadPtr)
{
//write pointer has caught up to read, increment read ptr
txReadPtr = (txReadPtr + 1) % TX_BUFFER_SIZE;
//Set overrun flag to FALSE
overrun = FALSE;
}
writePtr++;
length--;
}
//Make sure that Data is being transmitted
ensureTxCycleStarted();
return overrun;
}
void ensureTxCycleStarted()
{
WORD oldPtr = 0;
if(IS_UART_TX_IDLE() && !isTxBufEmpty())
{
//write one byte to start UART transmit cycle
oldPtr = txReadPtr;
txReadPtr = (txReadPtr + 1) % TX_BUFFER_SIZE;//Preincrement pointer
//Note: if pointer is incremented after U2TXREG write,
// the interrupt will trigger before the increment
// and the first piece of data will be retransmitted.
U2TXREG = 0xFF & (unsigned int)txbuf[oldPtr];
}
}
Edit
There are two ways that throttling could be implemented as I see it:
Enforce a time delay in between UART byte to be written that puts an upper limit on data throughput.
Keep a running tally of bytes transmitted over a certain time frame and if the maximum number of bytes is exceeded for that timespan create a slightly longer delay before continuing transmission.
Either option would theoretically work, its the implementation I'm wondering about.
Maybe a quota approach is what you want.
Using a periodic interrupt of relevant timescale, add a quota of "bytes to be transmitted" to a global variable to a point that you don't go over some level adjusted for the related deluge.
Then just check if there is quota before you come to send a byte. On new transmission there will be an initial deluge but later the quota will limit the transmission rate.
~~some periodic interrupt
if(bytes_to_send < MAX_LEVEL){
bytes_to_send = bytes_to_send + BYTES_PER_PERIOD;
}
~~in uart_send_byte
if(bytes_to_send){
bytes_to_send = bytes_to_send - 1;
//then send the byte
If you have a free timer, or if you can use an existing one, you could do some kind of "debounce" of the bytes sent.
Imagine you have this global var, byte_interval and you have a timer overflowing (and triggering the ISR) every microsecond. Then it could look something like this:
timer_usec_isr() {
// other stuff
if (byte_interval)
byte_interval--;
}
And then in the "putchar" function, you could have something like:
uart_send_byte(unsigned char b) {
if (!byte_interval) { // this could be a while too,
// depends on how you want to structure the code
//code to send the byte
byte_interval = AMOUNT_OF_USECS;
}
}
I'm sorry to not look much into your code so I could be more specific.
This is just an idea, I don't know if it fits for you.
First, there's two types of serial flow control in common use.
CTS/RTS handshaking ('hardware flow control')
XON/XOFF ('software flow control')
You say CTS is on, but you might want to see if XON/XOFF can be enabled in some way.
Another approach if you can configure it is simply to use a lower baud rate. This obviously depends on what you can configure on the other end of the link, but it's usually the easiest way of fixing problems when devices aren't able to cope with higher speed transfers.
Timer approach which adds delay to Tx at specific time:
Configure a free running timer at an appropriate periodic rate.
In the timer ISR, toggle a bit in a global state variable (delayBit)
In the UART ISR, if delayBit is high and delayPostedBit is low, then exit the TX ISR without clearing the TX interrupt flag and set a bit in a global state variable (delayPostedBit). If delayBit is low, then clear delayPostedBit. The result is to cause a delay equal to one ISR schedule latency, since the ISR will be entered again. This is not a busy-wait delay so won't affect the timing of the rest of the system.
Adjust the period of the timer to add latency at appropriate intervals.
I know that this topic (DMA & SPI) has already been talked about on numerous threads in the microchip forum, actually i've read all the 15 pages in result of the search with keyword "dma" and read all the topics about dma & spi.
And I am still stuck with my problem I hope someone can help me :)
Here is the problem.
My chip is a PIC32MX775F512H.
I am trying to receive (only receive) data using SPI via DMA.
Since you cannot "just" receive in SPI, and that the SPI core starts toggling the SPI clock only if you write into the SPIBUF (SPI1ABUF for me) I am trying to receive my data using 2 DMA channels.
DMA_CHANNEL1 for the transmitting part.
DMA_CHANNEL2 for the receiving part.
I copy pasted the code from http://www.microchip.com/forums/tm.aspx?tree=true&high=&m=562453&mpage=1#
And tried to make it work without any luck. It only receives several bytes (5 or 6).
I've set the Event Enable Flags to DMA_EV_BLOCK_DONE for both dma channels, no interrupt occurs.
Do you have any idea ?
Here is the code I am using :
int Spi_recv_via_DMA(SPI_simple_master_class* SPI_Port, int8u *in_bytes, int16u num_bytes2)
{
DmaChannel dmaTxChn=DMA_CHANNEL1;
DmaChannel dmaRxChn=DMA_CHANNEL2;
SpiChannel spiTxChn=SPI_Port->channel;
int8u dummy_input;
DmaChnOpen(dmaTxChn, DMA_CHN_PRI3, DMA_OPEN_DEFAULT);
DmaChnOpen(dmaRxChn, DMA_CHN_PRI3, DMA_OPEN_DEFAULT);
DmaChnSetEventControl(dmaTxChn, DMA_EV_START_IRQ_EN | DMA_EV_START_IRQ(_SPI1A_RX_IRQ));
DmaChnSetEventControl(dmaRxChn, DMA_EV_START_IRQ_EN | DMA_EV_START_IRQ(_SPI1A_RX_IRQ));
DmaChnClrEvFlags(dmaTxChn, DMA_EV_ALL_EVNTS);
DmaChnClrEvFlags(dmaRxChn, DMA_EV_ALL_EVNTS);
DmaChnSetEvEnableFlags(dmaRxChn, DMA_EV_BLOCK_DONE);
DmaChnSetEvEnableFlags(dmaTxChn, DMA_EV_BLOCK_DONE);
//SpiChnClrTxIntFlag(spiTxChn);
//SpiChnClrRxIntFlag(spiTxChn);
DmaChnSetTxfer(dmaTxChn, tx_dummy_buffer, (void *)&SPI1ABUF, num_bytes2, 1, 1);
DmaChnSetTxfer(dmaRxChn, (void *)&SPI1ABUF, in_bytes, 1, num_bytes2, 1);
while ( (SPI1ASTAT & SPIRBE) == 0)
dummy_input = SPI1ABUF;
SPI1ASTAT &= ~SPIROV;
DmaRxIntFlag = 0;
DmaChnEnable(dmaRxChn);
DmaChnStartTxfer(dmaTxChn, DMA_WAIT_NOT, 0);
while(!DmaRxIntFlag);
return 1;
}
with those two interrupt handlers :
// handler for the DMA channel 1 interrupt
void __ISR(_DMA1_VECTOR, ipl5) DmaHandler1(void)
{
int evFlags; // event flags when getting the interrupt
//LED_On(LED_CFG);
INTClearFlag(INT_SOURCE_DMA(DMA_CHANNEL1)); // acknowledge the INT controller, we're servicing int
evFlags=DmaChnGetEvFlags(DMA_CHANNEL1); // get the event flags
if(evFlags&DMA_EV_BLOCK_DONE)
{ // just a sanity check. we enabled just the DMA_EV_BLOCK_DONE transfer done interrupt
DmaTxIntFlag = 1;
DmaChnClrEvFlags(DMA_CHANNEL1, DMA_EV_BLOCK_DONE);
}
// LED_Off(LED_CFG);
}
void __ISR(_DMA2_VECTOR, ipl5) DmaHandler2(void)
{
int evFlags; // event flags when getting the interrupt
INTClearFlag(INT_SOURCE_DMA(DMA_CHANNEL2)); // acknowledge the INT controller, we're servicing int
evFlags=DmaChnGetEvFlags(DMA_CHANNEL2); // get the event flags
if(evFlags&DMA_EV_BLOCK_DONE)
{ // just a sanity check. we enabled just the DMA_EV_BLOCK_DONE transfer done interrupt
DmaRxIntFlag = 1;
DmaChnClrEvFlags(DMA_CHANNEL2, DMA_EV_BLOCK_DONE);
}
}
So I end up waiting forever at the line : while(!DmaRxIntFlag);
I have put breakpoints in the interrupt vectors, they are never called.
This is the state of several registers during the ever lasting wait :
DMACON 0x0000C800
DMASTAT 0x00000001
I am using SPI1A port, so SPI1ABUF and _SPI1A_RX_IRQ
DCH1SPTR 0x5
DCH1SSIZ 0x2B
DCH2DPTR 0x6
DCH2DSIZ 0x2B
DCH2CON 0x00008083
DCH2ECON 0x1B10
DCH2INT 0x00800C4
DCH2SSA 0x1F805820
DCH2DSA 0x00000620
Channel 1 is used to transmit
Channel 2 is used to receive
You are missing these:
INTEnable(INT_SOURCE_DMA(dmaTxChn), INT_ENABLED); // Tx
INTEnable(INT_SOURCE_DMA(dmaRxChn), INT_ENABLED); // Rx
rigth before
DmaRxIntFlag = 0;
DmaChnEnable(dmaRxChn);
DmaChnStartTxfer(dmaTxChn, DMA_WAIT_NOT, 0);
Good luck!
Are you using the SPI in slave mode? or you are on master mode, trying to read some response for a command?
Have you check the silicon errata for this chip? The dspic 33fj family had an issue where SPI slave mode simply didn't work.
Other than that, I don't think it is a good idea to busy wait for DmaRxIntFlag change. You should configure the DMA transfer and continue with your main loop. The DMA will trigger the interrupt handler.
Hope this helps.