Audio Transfer Callbacks not executing with STM32F469I-Discovery - c

I'm new to embedded programming and I'm currently working on a project with an STM32F469I-discovery board. I'm using eclipse with the ARM tool chain and the supplied drivers. I'm getting stuck on playing a binary audio file flashed into the chip at a specific address. I've very simply based my code on some of the example files, although very much cut down as I'm just trying to get it to work.
At the moment, the code works up to the point where it plays the buffer, but then it appears to get stuck. The buffer is playing in a loop (I've changed the size of the buffer to confirm this) and you can hear it, but that's all that happens. The transfer interrupt callbacks never execute, and hence the buffer does not refil and the full sample is never played.
I've tried using an external interupt to refil the buffer, but when I try this, it gets stuck. I've also tried to debug it by turning on LEDs, but this has confirmed that effectively it gets stuck shortly after playing the sample. The infinite while loop never executes, and the transfer interrupts never execute.
My question is - why is it getting stuck and why are the interrupts not being triggered?
Any help would be greatly appreciated!
#include "main.h"
static void SystemClock_Config(void);
#define AUDIO_FILE_ADDRESS 0x08010000
#define AUDIO_FILE_SIZE (180*1024)
#define PLAY_HEADER 0x2C
#define PBSIZE 4096
uint16_t PlayBuff[PBSIZE];
int OFFSET = 0;
int TransferState = 0;
int CycleCount1 = 1;
int CycleCount2 = 1;
int main(void)
{
uint32_t PlaybackPosition = PBSIZE + PLAY_HEADER;
HAL_Init();
/* Configure the system clock to 180 MHz */
SystemClock_Config();
// Fill the buffer first time round
for(int i=0; i <= PBSIZE; i++)
{
PlayBuff[i]=*((__IO uint16_t *)(AUDIO_FILE_ADDRESS + PLAY_HEADER + i));
}
BSP_AUDIO_OUT_Init(2,50,AUDIO_FREQUENCY_16K );
BSP_AUDIO_OUT_Play(PlayBuff,PBSIZE);
while(1){
if(TransferState==1){
// refill the first part of the buffer
TransferState=0;
OFFSET = CycleCount1*PBSIZE;
for(int i=0; i <= PBSIZE/2; i++){
PlayBuff[i]=*((__IO uint16_t *)(AUDIO_FILE_ADDRESS + PLAY_HEADER + OFFSET));
}
CycleCount1++;
}
if(TransferState==2){
// refill the second part of the buffer
OFFSET = CycleCount2*PBSIZE+PBSIZE;
TransferState=0;
for(int i=PBSIZE/2; i <= PBSIZE; i++){
PlayBuff[i]=*((__IO uint16_t *)(AUDIO_FILE_ADDRESS + PLAY_HEADER + OFFSET));
}
CycleCount2++;
}
}
}
void BSP_AUDIO_OUT_TransferComplete_CallBack(void)
{
TransferState=2;
}
void BSP_AUDIO_OUT_HalfTransfer_CallBack(void){
TransferState=1;
}
In the interests of identifying the cause of the problem, I've further trimmed down the code so that all it does is play the buffer. I've removed the interrupt calls to try and identify what's causing the issue.
What it should do is configure the system clock, fill the buffer, initialise the audio, then turn on the LED. It should then play the audio buffer, wait 1s, then turn off the LED. It plays the buffer in a loop (as it should as it's in circular mode) but then gets stuck and never turns off the LED. I've tried running it in normal mode but it simply plays the buffer once, and then gets stuck.
This leads me to think I've configured/filled the buffer incorrectly.
#include "main.h"
static void SystemClock_Config(void);
#define AUDIO_FILE_ADDRESS 0x08010000
#define PLAY_HEADER 0x17569
#define PBSIZE 4096
uint16_t PlayBuff[PBSIZE];
int main(void)
{
BSP_LED_Init(LED1);
HAL_Init();
/* Configure the system clock to 180 MHz */
SystemClock_Config();
// Fill the buffer first time round
for(int i=0; i <= PBSIZE; i++)
{
PlayBuff[i]=*((__IO uint16_t *)(AUDIO_FILE_ADDRESS + PLAY_HEADER + i));
}
BSP_AUDIO_OUT_Init(OUTPUT_DEVICE_HEADPHONE ,50,AUDIO_FREQUENCY_44K );
BSP_LED_On(LED1);
BSP_AUDIO_OUT_Play(PlayBuff,PBSIZE);
HAL_Delay(1000);
BSP_LED_Off(LED1);
}
Furthermore, I've also found instances of other people online having similar issues although with no solution so far. Any help would be greatly appreciated.

I've found the problem. I hadn't set the DMA interupt in the stm32f4xx_it.c.
void DMA2_Stream3_IRQHandler(void)
{
HAL_DMA_IRQHandler(haudio_out_sai.hdmatx);
}
I've also found that when using BSP_AUDIO_OUT_Play() the sound is wrong with something bassy fluctating underneath it.
Using the following worked correctly.
/* Start the playback */
if(0 != audio_drv->Play(AUDIO_I2C_ADDRESS, NULL, 0))
{
Error_Handler();
}
if(HAL_OK != HAL_SAI_Transmit_DMA(&haudio_out_sai, (uint8_t *) PlayBuff, PLAY_BUFF_SIZE))
{
Error_Handler();
}

I am not familiar with the specific board, from what i see possibly you need to define TransferState as volatile.
volatile int TransferState = 0;
It is quite common issue in embedded and multi-threaded development. The compiler can optimize the variable TransferState in a register so it never actually sees the update from a different context(the callback). Volatile prevents it from doing so

Related

Pic 18f4550 capture mode

I would like to measure a pulse using the pic 18f4550 in capture mode, this pulse is generated by the pic microcontroller itself, for this I use a function which plays the role of the XOR logic gate (you find the function that I've used below). with RC0 and RC2 the inputs and RC6 the signal output. the pulse leaving RC6 enters ccp2 to be measured.
The problem I found is that the ccp2 cannot detect the impulse generated by the microcontroller. I don't know if there are any conditions to connect the pins of the microcontroller or something.
If anyone has an answer or a hint to fix this, I will be grateful!
and if you have any questions feel free to ask .thanks !!
UPDATE: I changed some instructions in the code, now the RC6 output provides a signal. but my LCD does not display anything. the RC6 output is present below.
UPDATE 2: the while(1) in the xor() function blocking the rest of my program, so the program never get out of xor() and my LCD wont display anything. when I don't use the while loop in xor () my RC6 produce anything, the same for the LCD.
I don't know where the problem is, I did everything in my power to find the bug . but the system still not working!!!
I will leave the program as it is, so new readers can understand what I am talking about.
#include <stdio.h>
#include <stdlib.h>
#include "osc_config.h"
#include "LCD_8bit_file.h"
#include <string.h>
unsigned long comtage,capt0,x;
char pulse[20];
char cosinus[20];
float period,dephTempo,deph,phi;
void init (){
IRCF0 =1; /* set internal clock to 8MHz */
IRCF1 =1;
IRCF2 =1;
PIE2bits.CCP2IE=1;
PIR2bits.CCP2IF=0;
CCPR2 =0; /*CCPR1 is capture count Register which is cleared initially*/
T3CONbits.RD16=1;
T3CKPS0=0;
T3CKPS1=0;
TMR3CS=0;
TMR3IF=0;
T3CCP2=0; /*Timer3 is the capture clock source for CCP2*/
}
void xor()
{
while(1)
{
if (PORTCbits.RC0==PORTCbits.RC2)
{
PORTCbits.RC6=0;
}
else if (PORTCbits.RC0!=PORTCbits.RC2)
{
PORTCbits.RC6=1;
}
}
}
void main()
{
TRISCbits.TRISC0=1;
TRISCbits.TRISC2=1;
TRISCbits.TRISC6=0;
xor();
LCD_Init();
while(1)
{
CCP2CON = 0b00000101;
PIR2bits.CCP2IF = 0;
TMR3ON = 0;
TMR3 = 0;
while (!PIR2bits.CCP2IF);
TMR3ON = 1;
CCP2CON = 0b00000100;
PIR2bits.CCP2IF = 0;
while (!PIR2bits.CCP2IF);
comtage = CCPR2;
dephTempo = (((float)comtage /30.518)/65536 );
sprintf(pulse,"%.3f ",dephTempo);
LCD_String_xy(0,0,"the pulse width is : ");
LCD_String_xy(2,9,pulse);
}
}

pulse width using pic 18f4550

I want to measure pulse duration using only one CCP model in capture mode with a pic 18f4550, so I try to detect the rising edge in first time, when a rising edge is detected the timer1 turn on and the capture mode change to falling edge, with this method I have to measure the pulse width, but the code I use doesn't work well!!
it was good when I used two CCP model.
if anyone could help, I will be grateful.
#include <stdio.h>
#include <stdlib.h>
#include "osc_config.h"
#include "LCD_8bit_file.h"
#include <string.h>
void main()
{
unsigned long comtage;
unsigned long DEPHASAGE[20];
float Deph_tempo;
TRISCbits.TRISC2=1;
IRCF0=1;
IRCF1=1;
IRCF2=1;
LCD_Init();
LCD_String_xy(0,0,"Deph.tempo");
PIE1bits.CCP1IE=1;
PIR1bits.CCP1IF=0;
CCP1CON=0b00000101;
CCPR1=0;
T1CONbits.RD16=1;
T1CKPS0=0;
T1CKPS1=0;
TMR1CS=0;
TMR1IF=0;
TMR1=0;
while(1)
{
if(PIR1bits.CCP1IF==1){
TMR1ON=1;
PIR1bits.CCP1IF=0;
CCP1CON=0b00000100;
while(!(PIR1bits.CCP1IF==1))
comtage= TMR1;
PIR1bits.CCP1IF=0;
Deph_tempo = (((float)comtage /30.518)/65536 );
sprintf(DEPHASAGE,"%.5f ",Deph_tempo);
LCD_String_xy(2,0,DEPHASAGE);
}
TMR1=0;
TMR1ON=0;
CCP1CON=0b00000101;
}
}
Reducing the number of instructions being exceuted while waiting for CCP1IF will increase precision. Have you tried this?
// ...
while (1)
{
CCP1CON = 0b00000101;
PIR1bits.CCP1IF = 0;
TMR1ON = 0;
TMR1 = 0;
// if your comms with the LCD use interrupts:
//
// disable interrupts here
while (!PIR1bits.CCP1IF)
;
TMR1ON = 1;
CCP1CON = 0b00000100;
PIR1bits.CCP1IF = 0;
while (!PIR1bits.CCP1IF)
;
compte = TMR1;
// if your comms with the LCD use interrupts:
//
// enable interrupts here
// refresh display
// if your comms with the LCD use interrupts:
//
// you may want to add a small delay here, to
// allow for comms to the LCD to end.
// this may not be necessary, depending on the
// signal frequency.
}
// ...
If that doesn't work, you should check that the LCD is NOT using interruots.
If it does, you should:
disable interrupts before reading a sample
keep interrupts disabled while timing
enable interrupts before updating the display
add a delay before taking the next sample, the delay should be long enough for the LCD buffer to empty.
That's for a solution without using interrupts... I think you'll get better results using pin change interrupts and a free running timer.
EDIT: After writing this solution, I found the bug in your code, around these lines of code:
while(!(PIR1bits.CCP1IF==1)) // this is missing a ;
comtage= TMR1; // this line gets executed in the loop
// and adds instructions to the
// loop, this probably more than
// halves the precision of your
// results.
// the imprecision is increased with
// your code that runs after the if block

PIC16LF1824 SPI Slave Interface

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...

AVR USART Programming

I am currently working on a project where we have to use an AVR ATMEGA328 micro-controller, specifically the USART peripheral, to control 8 LED's. We have to send commands to the micro-controller that will turn on, off, and blink the LED's at different rates. I have written a program in C that I think will do the job, but I would like someone to look at it and help me fix any mistakes that I may have. Your help will be greatly appreciated!
*P.S. Each command in the commands array associates with its corresponding LED state in the LED array. The LED's are connected to PORTB of the micro-controller.
#include <avr/io.h>
#include <util/delay.h>
#include <avr/interrupt.h>
/* Arrays that contain all input commands and LED states */
const char *commands[] = {"ON0","ON1","ON2","ON3","ON4","ON5","ON6","ON7","ON8","OFF0","OFF1","OFF2","OFF3","OFF4","OFF5","OFF6","OFF7","OFF8","BLINK0","BLINK1","BLINK2","BLINK3","BLINK4","BLINK5","BLINK6","BLINK7","BLINK8","STOP"\0}
int LEDs[28] = {0X01,0X02,0X04,0X08,0X10,0X20,0X40,0X80,0XFF,0XFE,0XFD,0XFB,0XF7,0XEF,0XDF,0XBF,0X7F,0,0X01,0X02,0X04,0X08,0X10,0X20,0X40,0X80,0XFF,0}
int i;
int j;
int Blinky(int j); // Function to execute blinking commands where j is the argument
{
PORTB = LEDs[j];
_delay_ms[250 + (j-18) * 50]; /* Calculates blinking delay times */
PORTB = 0;
_delay_ms[250 + (j-18) * 50];
}
int main(void)
{
DDRB=0XFF; // PORTB is set to output
DDRD=0X02; // Turns on the transmit pin for the USART in PORTD
/* Setup USART for 9600,N,8,1 */
USCR0B = 0X18;
USCR0C = 0X06;
UBRR0 = 51;
sei(); // Enable Global Interrupts
char input;
if(UCSR0A & 0X80) /* Reads data from the USART and assigns the contents to the character input */
input = UDR0;
j=28;
char cmd;
cmd = *commands[i];
for(i=0; i<28; i++)
{
if(input==cmd) /* If the contents of UDR0 are equal to one of the commands */
j = i;
}
while(1)
{
if(j<18)
PORTB=LEDs[j]; // Executes the "ON" and "OFF" commands
else if(j<27)
Blinky(j); // Executes the blinking command by calling the Blinky function
else if(j=27)
PORTB=0; // Executes the "STOP" command
else
PORTB=0; // Accounts for typing errors
}
return(0);
}
There is a lot wrong with this program, but code review is not the purpose of Stack Overflow. See the FAQ for how to ask an appropriate question.
That said, some of the obvious problems are:
The _delay_ms() function needs to be called with a compile-time constant. It won't work correctly if the parameter needs to be calculated at run-time.
if you don't read any char from USART, then you still go through the rest of the loop.
char cmd declares a character variable, but then you assign a pointer to it.
i is used before it is set to a meaningful value.
input== cmd will likely never be true as one side is a character and the other is a pointer.
This question will likely close soon. Good luck, and come back if you have a question better suited to Stack Overflow.

cycling through leds

Please help me with this code, it is making me crazy. This is a very simple program with 8-bit timer, cycling through all 8 leds (one-by-one). Am using ATSTK600 board.
My timers are working well, I think there is some problem with the loops (when I debug this program using avr studio-gcc, I can see all the leds working as I want but when I transfer it on board...leds don't blink). Am going crazy with this type of behavior.
Here is my code:
#include <avr/io.h>
#include <avr/interrupt.h>
volatile unsigned int intrs, i, j = 0;
void enable_ports(void);
void delay(void);
extern void __vector_23 (void) __attribute__ ((interrupt));
void enable_ports()
{
DDRB = 0xff;
TCCR0B = 0x03;
TIMSK0 = 0x01;
//TIFR0 = 0x01;
TCNT0 = 0x00;
//OCR0A = 61;
intrs = 0;
}
void __vector_23 (void)
{
for(i = 0; i<=8; i++)
{
while(1)
{
intrs++;
if(intrs >= 61)
{
PORTB = (0xff<<i);
intrs = 0;
break;
}
}
}
PORTB = 0xff;
}
int main(void)
{
enable_ports();
sei();
while(1)
{
}
}
Your interrupt routine is flawed. intrs counts only the number of times the loop has executed, not the number of timer interrupts as its name suggests. 61 iterations of that loop will take very little time. You will see nothing perceivable without an oscilloscope.
The following may be closer to what you need:
void __vector_23 (void)
{
intrs++;
if(intrs > 60)
{
intrs = 0;
PORTB = (0xff<<i);
i++ ;
if(i == 8 )
{
i = 0 ;
PORTB = 0xff;
}
}
}
Although setting the compare register OCR0A to 61 as in your commented out code would avoid the need for the interrupt counter and reduce unnecessary software overhead.
Are you sure that the code downloaded to the board is not optimized?
Have you attached volatile attribute to the PORTB identifier?
Is there a way for you to slow down the code (outside the debugger)? Any chance it's running but fast that you don't see it?
Can you verify that your intended code is in fact running (outside the debugger)?
When interrupt occurs, handler very quickly counts 62*9 times and finally sets PORTB to 0x00, so leds do only very short flash which is not visible. You see it in sumulator just because it runs slower and do not emulate visual dimming effect of fast port switching. Program has a design flaw: it tries to do full blinking cycle in single interrupt. That's wrong--only a single step should be performed in interrupt call. So handler should look like this:
void __vector_23 (void)
{
intrs++;
if(intrs >= 61)
{
PORTB = (0xff<<i);
intrs = 0;
i++;
if(i>8) i = 0;
}
}
Try this.
There is guidelin on interrupts handlers: Interrupt handler should be as fast and short as possible. Do not perform complex tasks in interrupts (cycle loop is one of them, if you get cycle in interrupt, try to remove it). Do not wait or delay in interrupts.
If you're seeing the behaviour you want when debugging with avr studio-gcc, then that gives you some confidence that your program is "good" (for some sense of the word "good"). So it sounds as though you need to focus on a different area: what is the difference between your debug environment and your stand-alone download?
When doing a stand-alone download, do you know if your program is running at all?
Are the LEDs blinking, or turning on at all? You don't explicitly say in your question, but that question could be very relevant to the debugging process. Does it look like the right behaviour, running at a different speed? If so, then your program is probably not doing some sort of initialisation that the debugger was doing.
When doing a stand-alone download, is the program being compiled with different settings compared to the debug version? Perhaps compiler optimisation settings are changing your program's timing characteristics.
(Your question would be better if you gave more detail about what the stand-alone download is doing. In general, it is hard for someone to debug a remote system when they're given few or no details about what is happening. Do all/some of the LEDs turn on at all?)

Resources