I am using an RN42-XV bluetooth module to send characters to the PIC24F from a computer.
The module is connected/paired correctly and the characters that are sent across are also correct (used an oscilloscope).
This is how it is being initialized:
void initUART(){
//Peripheral Pin Mapping
RPINR19bits.U2RXR = 5; //pin 14 UART Receive
RPOR5bits.RP11R = 3; //pin 17 UART Transmit
//Configuring the UART
U2BRG = BRGVAL;
U2MODEbits.UARTEN = 1;
U2MODEbits.UEN = 0;
U2MODEbits.PDSEL = 0;// 8 bit no parity
U2MODEbits.STSEL = 0; // 1 stop bit
U2STAbits.UTXEN = 0;
U2STAbits.URXISEL = 0;
//Putting the UART interrupt flag down.
IFS1bits.U2RXIF = 0;
}
I am also using this function to get the contents of the buffer:
int waitForChar(){
int receivedChar;
// Use the UART RX interrupt flag to wait until we recieve a character.
while(IFS1bits.U2RXIF == 1){
// Clear the UART RX interrupt flag to we can detect the reception
// of another character.
IFS1bits.U2RXIF = 0;
// U2RXREG stores the last character received by the UART. Read this
// value into a local variable before processing.
receivedChar = U2RXREG;
}
return receivedChar;
}
The problem is that the program never goes into the while loop inside the function waitForChar() because the UART interrupt flag is never raised by the hardware.
I have tried different PIC24Fs but all run into the same issue.
The function type is declared as void so it does not return anything. You should be getting compiler warning if you try to assign its return value. Moreover it does not wait for a char. It is "non blocking" that is it returns anyway, but you need a return value to tell you whether it has a char or whether it does a not. If you want it to wait and return a char, it could be like this
int waitForChar(){ // declare a return type
int receivedChar;
while(IFS1bits.U2RXIF == 0); // wait
receivedChar = U2RXREG;
IFS1bits.U2RXIF = 0; // clear status
return receivedChar;
}
I notice a couple of things:
you enable the module (UARTEN) before entirely configuring it.
Shouldn't U2STA.URXDA used as flag to test for receiving?
You don't configure several bits in both registers. That is ok though if you are absolutely sure the startup state is what you like.
The UART initialization code was missing this line:
AD1PCFG = 0xFFFF
The ADC flags have priority over UART. This line disables them.
Related
I have code written by someone else (for a dsPIC33FJ128MC706A) - which initializes UART1 & PWMs and uses a busy wait in the U1Rx interrupt to get 14 bytes, and timer1 interrupts to update PWM registers if new data arrives (debugged - it enters the _U1RXInterrupt when data is sent)
I tried modifying it to use DMA instead of busy wait. I changed nothing in the initialization, but called the following function after initialization of UART:
unsigned char received_data1 [0x0f] __attribute__((space(dma)));
unsigned char received_data2 [0x0f] __attribute__((space(dma)));
void dma_init(void)
{
DMA0REQ = 0x000b; // IRQSEL = 0b0001011 (UART1RX))
DMA0PAD = (volatile unsigned int) &U1RXREG;
DMA0STA = __builtin_dmaoffset(received_data1);
DMA0STB = __builtin_dmaoffset(received_data2);
DMA0CNT = 0xe;//15 bytes in
DMA0CON = 0x0002; //continuous ping-pong + post-increment
IFS0bits.DMA0IF = 0;
IEC0bits.DMA0IE = 1;
DMA0CONbits.CHEN = 1;
}
This is based on example 22-10 in the FRM with a few slight changes (UART1 receiver instead of UART2). I can't seem to find other examples that don't use library functions (which I didn't even know existed up until I came across those examples).
(Also, to be consistent with the code I got, I put in _ISR after void instead of making interrupt an attribute, but that didn't change anything)
In the example, there was no interrupt for the UART1 receiver - but according to the FRM, it should be enabled. Made sense to me that I don't need to clear the interrupt flag myself (it would kind-of defeat the purpose of DMA if I needed to use the CPU for it).
And yet, when I send data, the breakpoint on DMA0Interrupt doesn't trigger, but the one on the default interrupt does, and I have PWMIF=1 and U1RXIF=1. I also tried commenting out the default ISR and adding a _U1ErrInterrupt even though examining the flags I didn't notice any error flags being raised, just the normal U1RXIF.
I don't understand why this would happen. I haven't changed the PWM code at all, and the PWM flag isn't raised in the original code when I place a breakpoint in the _U1RxInterrupt. I don't even understand how a DMA set-up could cause a PWM error.
The U1RXIF flag seems to be telling me the DMA didn't handle the incoming byte (I assume it's the DMA that clears the flag), which once again relates to the question "what did I do wrong in this DMA setup"
UART initialization code:
in a general initialization function (called by main())
TRISFbits.TRISF2 = 1; /* U1RX */
TRISFbits.TRISF3 = 0; /* U2TX */
also called by main:
void UART1_init_USB (void)
{
/* Fcy 7.3728 MHZ */
U1MODE = 0x8400; /* U1ATX Transmit status and control register */
U1STA = 0x2040; /* Receve status and control register Transmit disabled*/
U1BRG = 0x000f; /* Baund rate control register 115200 bps */
IPC2bits.U1RXIP = 3; /* Receiver Interrupt Priority bits */
IEC0bits.U1RXIE = 1; /* Enable RS232 interrupts */
IFS0bits.U1RXIF = 0;
}
(comments by original code author)
I am observing some odd behavior in my PIC code where I copy data from a
global variable that is written by an ISR into two different buffers
consecutively, but the value only seems to successfully get written into
one of the two buffers. When I try this with a different variable that is
not written by an ISR, it correctly gets copied into both buffers.
My setup is a PIC16LF15355 using the XC8 compiler version 1.45. I am
configuring one of the PIC MSSP modules as a SPI interface to write
measurement data and switch states to an RF transceiver. I have a another
remote RF transceiver that receives and processes the data with a
PIC16LF15355 via a SPI interface. For both PICs, I am configuring the
second MSSP module as an I2C interface connected to a Raspberry Pi for
debugging purposes. Any data that is written to the SPI buffer on the
transmitting end is also copied to the I2C buffer so that it can be
periodically read to see what is being sent. Likewise, the data read
into the SPI buffer on the receiving end is copied to an I2C buffer so
that it can be read periodically to see what is being received.
There is a switch input on the transmitting end that is debounced with
a timer ISR (timer0_handler in the code below). The state gets written
into sw1State by this ISR. When it comes time to transmit data, the
switch state and other measurement data gets copied into the I2C buffer
and then the I2C buffer gets copied into the SPI buffer for transmission.
When I sample the I2C buffer contents periodically from the Pi, I can see
switch state changing in response to presses of the switch. But when I
sample the I2C buffer contents periodically on the receiving end, I only
ever see the switch state default value (1).
What is strange is that if I change the code to copy the value from a
variable that is not written by an ISR, the value shows up correctly on
the receiving end. But copy from the I2C buffer to the SPI buffer is
just a generic copy of one array to another, and all other measurement
data shows up correctly on the receiving end. The two buffers should
be identical. And there is nothing in my code that modifies the SPI
buffer between the time when it gets copied from the I2C buffer and
when the SPI buffer gets written out to the transceiver.
A stripped down copy of the code is included below. I've been looking
at this for several days and I just don't see what is causing this.
volatile uint8_t sw1State; // current switch 1 state
volatile uint8_t sw1TimeExpired; // true if switch 1 debounce time expired
uint8_t spiOutBuf[13]; // SPI output buffer
uint8_t spiInBuf[13]; // SPI input buffer
uint8_t i2cBuff[13]; // I2C input/output buffer
void interrupt main_ISR( void ) {
I2C_slave_handler();
timer0_handler();
switch_input_handler();
}
int main( void ) {
... device initialization code omitted for brevity
sw1State = 1;
sw1TimeExpired = 1;
for ( ; ; )
{
// Read the ADC values and add to the I2C buffer (for debugging).
for ( uint8_t chan = 0; chan < 9; chan++ )
{
i2cBuff[chan + 1] = ADC_read( chan );
}
// Read the switch status and add to the I2C buffer.
i2cBuff[10] = sw1State;
// Load the write transmit payload command into the SPI buffer.
spiOutBuf[0] = CMD_W_TX_PAYLOAD;
// Copy the payload data from the I2C buffer to the SPI buffer.
for ( uint8_t i = 1; i < 13; i++ )
{
spiOutBuf[i] = i2cBuff[i];
}
// Write the SPI buffer to the transceiver module payload register.
writeReadSPI( 13 );
// Clear the RB4 interrupt-on-change interrupt flag (IRQ change) and
// set CE high to initiate the transmit. Hold CE high until an ack
// is received or there is an ack timeout.
IOCBFbits.IOCBF4 = 0;
CE = 1;
__delay_us( 130 ); // ensure minimum state change transition time
... code here omitted which checks the transceiver status
__delay_ms( 80 );
}
}
void switch_input_handler( void ) {
// Check for switch 1 trigger.
if ( IOCCFbits.IOCCF6 == 1 )
{
IOCCFbits.IOCCF6 = 0; // clear IOC interrupt
// Process switch 1 trigger if debounce time has expired.
if ( sw1TimeExpired == 1 )
{
// Toggle between OFF state and ON state.
if ( sw1State == 1 )
{
sw1State = 2;
}
else
{
sw1State = 1;
}
// Load and restart Timer0 with 2-second counter value.
T0CON0bits.T0EN = 0;
TMR0H = 0xc2;
TMR0L = 0xf7;
T0CON0bits.T0EN = 1;
sw1TimeExpired = 0;
}
}
}
void timer0_handler( void ) {
if ( PIR0bits.TMR0IF == 1 )
{
PIR0bits.TMR0IF = 0; // clear the interrupt
T0CON0bits.T0EN = 0; // disable Timer0
if ( sw1TimeExpired == 0 )
{
sw1TimeExpired = 1; // Note the switch 1 timeout for switch handler.
// No state transition here. This just enables
// the next switch interrupt to change state.
}
}
}
void I2C_slave_handler( void ) {
... code omitted for brevity
}
From the code snippet it appears that the SPI channel is transmitting as a background task, while the I2C channel is transmitting in the foreground (interrupt handler). This could potentially result in a race condition and explain the behavior you are experiencing.
Recommend you try using a single transmit buffer for both channels, and set a variable to indicate when both channels have successfully sent the the data. Once both channels indicate data transmitted, then generate a fresh data packet/buffer for the next cycle.
I am trying to receive multiple bytes over SPI. The aim is when the master starts the SPI transfer, slave MCU is interrupted, and it should read the data via SPI and store it in an array, which will be then used by my application for other operations such as determining the device ID and the contents of the packet.
void interrupt __high_priority my_isr_high(void) {
if (PIR1bits.SSP1IF) { // Interrupt from SPI?
rx[buffer_pointer] = SSP1BUF; // Get data from MSSP and store in RX buffer
buffer_pointer++; // Next data
if (buffer_pointer < FRAME_SIZE) // Ended?
SSP1BUF = tx[buffer_pointer]; // Send next byte to SPI
else
buffer_pointer = FRAME_SIZE;
PIR1bits.SSP1IF = 0; // Clear interrupt flag
}
}
However, I am not receiving the 3 bytes correctly. I am sending the following from the master:
dataPacket[0] = 0x43; // Decimal 67
dataPacket[1] = 0x42; //66
dataPacket[2] = 0x41; //65
While I am receiving as follows from the ISR():
rx[0]: 67
rx[1]: 65
rx[2]: 67
Am I missing something or handling the SPI incorrectly?
This will really solve the issue that I am stuck with and maybe will also help others who what to rx multiple bytes.
I am sharing my codes here so that it helps to find the solution quickly. Also included is a .zip file for compiling. Check the Codes here
So far the above code did not work for me properly. Therefore, after a little bit of digging online and other forums I found the following way to read multiple bytes:
uint8_t SPI_ExchangeHandler(uint8_t byte){
static uint8_t i = 0;
for(i=0; i<3; i++){
SSP1BUF =0x00;
while(!SSP1STATbits.BF);
rx_buff[i]=SSP1BUF;
}
State = SEND;
return byte;
}
Although the above codes give me what expected (i.e, correct data packets in the ordered manner), however, it misses two SPI interrupts every time and then displays/captures the correct data. Hence, two sets of data are always lost and then the third one is received correctly.
Is something wrongly configured or missing?
Finally, I managed to receive all the 3 bytes correctly. Sharing the codes below:
My interrupt service routine that triggers the MCU when master SPI has data to send.
void interrupt INTERRUPT_InterruptManager (void){
if(PIE1bits.SSP1IE == 1 && PIR1bits.SSP1IF == 1)
{
SPI_ISR();
}
}
The SPI_ISR code was autogenerated by the MCC GUI.
void SPI_ISR(void)
{
SSP1BUF = SPI_xchgHandler(SSP1BUF);
}
void SPI_setExchangeHandler(uint8_t (* InterruptHandler)(uint8_t))
{
SPI_xchgHandler = InterruptHandler;
}
Then I handle the SPI via a custom function using SPI_setExchangeHandler() as follows:
#define FRAME_SIZE 10 // Frame fixed size
volatile static uint8_t rx_buff[FRAME_SIZE]; //RX buffer
uint8_t SPI_ExchangeHandler(uint8_t byte)
{
static uint8_t i = 0;
rx_buff[i]=SSP1BUF;
i++;
if(i <= 2){
rx_buff[i]=SSP1BUF;
SSP1BUF = 0x00;
while(!SSP1STATbits.BF){};
i++;
}
else{
i = 2;
}
PIR1bits.SSP1IF = 0; // Clear the SPI interrupt flag
State = SEND;
return byte;
}
And I print out the values as follows for debugging:
printf("CMD:%d \n", rx_buff[0]);
printf("BUF1: %d \n", rx_buff[1]);
printf("BUF2: %d \n\n", rx_buff[2]);
However, I am pretty sure this is not the best/optimized way to handle multiple bytes from SPI, therefore, if there is an alternative, share...
Using MPLAB X 1.70 with a dsPIC33FJ128GP802 microcontroller.
I've got an application which is collecting data from two sensors at different sampling rates (one at 50Hz, the other at 1000Hz), both sensor packets are also different sizes (one is 5 bytes, the other is 21 bytes). Up until now I've used manual UART transmision as seen below:
void UART_send(char *txbuf, char size) {
// Loop variable.
char i;
// Loop through the size of the buffer until all data is sent. The while
// loop inside checks for the buffer to be clear.
for (i = 0; i < size; i++) {
while (U1STAbits.UTXBF);
U1TXREG = *txbuf++;
}
}
The varying sized arrays (5 or 21 bytes) were sent to this function, with their size, and a simple for loop looped through each byte and outputted it through the UART tx register U1TXREG.
Now, I want to implement DMA to relieve some pressure on the system when transmitting the large amount of data. I've used DMA for my UART receive and ADC, but having trouble with transmit. I've tried both ping pong mode on and off, and one-shot and continuous mode, but whenever it comes to sending the 21 byte packet it messes up with strange values and zero value padding.
I'm initialising the DMA as seen below.
void UART_TX_DMA_init() {
DMA2CONbits.SIZE = 0; // 0: word; 1: byte
DMA2CONbits.DIR = 1; // 0: uart to device; 1: device to uart
DMA2CONbits.AMODE = 0b00;
DMA2CONbits.MODE = 1; // 0: contin, no ping pong; 1: oneshot, no ping pong; 2: contin, ping pong; 3: oneshot, ping pong.
DMA2PAD = (volatile unsigned int) &U1TXREG;
DMA2REQ = 12; // Select UART1 Transmitter
IFS1bits.DMA2IF = 0; // Clear DMA Interrupt Flag
IEC1bits.DMA2IE = 1; // Enable DMA interrupt
}
The DMA interrupt I'm just clearing the flag. To build the DMA arrays I've got the following function:
char TXBufferADC[5] __attribute__((space(dma)));
char TXBufferIMU[21] __attribute__((space(dma)));
void UART_send(char *txbuf, char size) {
// Loop variable.
int i;
DMA2CNT = size - 1; // x DMA requests
if (size == ADCPACKETSIZE) {
DMA2STA = __builtin_dmaoffset(TXBufferADC);
for (i = 0; i < size; i++) {
TXBufferADC[i] = *txbuf++;
}
} else if (size == IMUPACKETSIZE) {
DMA2STA = __builtin_dmaoffset(TXBufferIMU);
for (i = 0; i < size; i++) {
TXBufferIMU[i] = *txbuf++;
}
} else {
NOTIFICATIONLED ^= 1;
}
DMA2CONbits.CHEN = 1; // Re-enable DMA2 Channel
DMA2REQbits.FORCE = 1; // Manual mode: Kick-start the first transfer
}
This example is with ping pong turned off. I'm using the same DMA2STA register but changing the array depending on which packet type I have. I'm determining the packet type from the data to be sent, changing the DMA bytes to be sent (DMA2CNT), building the array same as before with a for loop, then forcing the first transfer along with re-enabling the channel.
It takes much longer to process the data for the large data packet and I'm starting to think the DMA is missing these packets and sending null/weird packets in its place. It seems to be polling before I build the buffer and force the first transfer. Perhaps the force isn't necessary for every poll; I don't know...
Any help would be great.
After a few days of working on this I think I've got it.
The main issue I experienced was that the DMA interrupt was being polled faster than previous transmission, therefore I was only getting segments of packages before the next package overwrote the previous. This was solved with simply waiting for the end of UART transmission with:
while (!U1STAbits.TRMT);
I managed to avoid the redundancy of recreating a new DMA with the package data by simply making the original data array the one recognised by the DMA.
In the end the process was pretty minimal, the function called every time a package was created is:
void sendData() {
// Check that last transmission has completed.
while (!U1STAbits.TRMT);
DMA2CNT = bufferSize - 1;
DMA2STA = __builtin_dmaoffset(data);
DMA2CONbits.CHEN = 1; // Re-enable DMA0 Channel
DMA2REQbits.FORCE = 1; // Manual mode: Kick-start the first transfer
}
Regardless of what the size of the package, the DMA changes the amount it sends using the DMA2CNT register, then it's simply re-enabling the DMA and forcing the first bit.
Setting up the DMA was:
DMA2CONbits.SIZE = 1;
DMA2CONbits.DIR = 1;
DMA2CONbits.AMODE = 0b00;
DMA2CONbits.MODE = 1;
DMA2PAD = (volatile unsigned int) &U1TXREG;
DMA2REQ = 12; // Select UART1 Transmitter
IFS1bits.DMA2IF = 0; // Clear DMA Interrupt Flag
IEC1bits.DMA2IE = 1; // Enable DMA interrupt
Which is one-shot, no ping-pong, byte transfer, and all the correct parameters for UART1 TX.
Hope this helps someone in the future, the general principle can be applied to most PIC microcontrollers.
I'm trying to recreate a project of writing to an SD card (using FatFS) for a dsPIC33FJ128GP802 microcontroller.
Currently to collect the date from the SPI I have a do/while that loops 512 times and writes a dummy value to the SPI buffer, wait for the SPI flag, then read the SPI value, like so:
int c = 512;
do {
SPI1BUF = 0xFF;
while (!_SPIRBF);
*p++ = SPI1BUF;
} while (--c);
I'm trying to recreate this using the DMA intterupts but it's not working like I had hoped. I'm using one DMA channel, SPI is in 8 bit mode for the time being, so DMA is in byte mode, it's also in 'null write' mode, and continuous without ping pong. My buffers are only one member arrays and the DMA is matched.
DMA2CONbits.CHEN = 0; //Disable DMA
DMA2CONbits.SIZE = 1; //Receive bytes (8 bits)
DMA2CONbits.DIR = 0; //Receive from SPI to DMA
DMA2CONbits.HALF = 0; //Receive full blocks
DMA2CONbits.NULLW = 1; //null write mode on
DMA2CONbits.AMODE = 0; //Register indirect with post-increment
DMA2CONbits.MODE = 0; //continuous mode without ping-pong
DMA2REQbits.IRQSEL = 10; //Transfer done (SPI)
DMA2STA = __builtin_dmaoffset(SPIBuffA); //receive buffer
DMA2PAD = (volatile unsigned int) &SPI1BUF;
DMA2CNT = 0; //transfer count = 1
IFS1bits.DMA2IF = 0; //Clear DMA interrupt
IEC1bits.DMA2IE = 1; //Enable DMA interrupt
From what I understand from the null write mode, the DMA will write a null value every time a read is performed. However, the DMA wont start until an initial write is performed by the CPU, so I've used the manual/force method to start the DMA.
DMA1CONbits.CHEN = 1; //Enable DMA
DMA1REQbits.FORCE = 1; //Manual write
The interrupt will now start, and runs without error. However the code later shows that the collection was incorrect.
My interrupt is simple in that all I'm doing is placing the collected data (which I assume is placed in my DMAs buffer as allocated above) into a pointer which is used throughout my program.
void __attribute__((interrupt, no_auto_psv)) _DMA2Interrupt(void) {
if (RxDmaBuffer == 513) {
DMA2CONbits.CHEN = 0;
rxFlag = 1;
} else {
buffer[RxDmaBuffer] = SPI1BUF;
RxDmaBuffer++;
}
IFS1bits.DMA2IF = 0; // Clear the DMA0 Interrupt Flag
}
When the interrupt has run 512 times, I stop the DMA and throw a flag.
What am I missing? How is this not the same as the non-DMA method? Is it perhaps the lack of the while loop which waits for the completion of the SPI transmission (while (!_SPIRBF);). Unfortunately with the null write mode automatically sending and receiving the SPI data I can't manually put any sort of wait in.
I've also tried using two DMA channels, one to write and one to read, but this also didn't work (plus I need that channel later for when I come to proper writing to the SD card).
Any help would be great!