USART receiver on STM32 - c

Hi I am currently working on USART communication trying to transmit and receive data from any GPIO pin.
I am succeed to transmit data at any baud-rate when it comes to receiving i got stuck at a point.
I was able to receive a character at a time. Pin is set as external falling edge interrupt used a RX pin.
But when i transmit a string like "test" from terminal to controller only "t" is received rest 3 character is garbage value. I was thinking that after receiving first character and saving it, the Interrupt is not triggered as fast for next character.
Many things are hard coded in this sample code for test purpose.
Here the sample code for receiver
void EXTI0_IRQHandler(void){
r0 = GPIOA->IDR;
delay_us(delay_time);
r1 = GPIOA->IDR;
delay_us(delay_time);
r2 = GPIOA->IDR;
delay_us(delay_time);
r3 = GPIOA->IDR;
delay_us(delay_time);
r4 = GPIOA->IDR;
delay_us(delay_time);
r5 = GPIOA->IDR;
delay_us(delay_time);
r6 = GPIOA->IDR;
delay_us(delay_time);
r7 = GPIOA->IDR;
delay_us(delay_time);
r8 = GPIOA->IDR;
delay_us(delay_time);
r9 = GPIOA->IDR;
delay_us(delay_time);
r1 = r1 & 0x00000001;
r2 = r2 & 0x00000001;
r3 = r3 & 0x00000001;
r4 = r4 & 0x00000001;
r5 = r5 & 0x00000001;
r6 = r6 & 0x00000001;
r7 = r7 & 0x00000001;
r8 = r8 & 0x00000001;
x |= r8;
x = x << 1;
x |= r7;
x = x << 1;
x |= r6;
x = x << 1;
x |= r5;
x = x << 1;
x |= r4;
x = x << 1;
x |= r3;
x = x << 1;
x |= r2;
x = x << 1;
x |= r1;
buff1[z++] = x;
EXTI->PR |= 0X00000001;
x=0;
return ;}
Thanks for your help.

The fundamental problem with your solution is that you are sampling the bits at the transition point rather then the bit centre. On detection of the START transition, you delay one bit period only, so sample r1 at the bit transition rather then the bit centre - this will almost certainly result in errors, especially at high speed where the edges may not be very fast. The first delay should be 1.5 bit periods long. (delay_time * 2 / 3) as illustrated below:
A second problem is that you unnecessarily delay after the STOP bit, which will cause you to miss the next START transition because it may occur before you clear the interrupt flag. Your work is done as soon as you have r8.
Sampling r0 and r9 serves no purpose you discard them in any case, and the state r0 is implicit in any event form the EXTI transition, and r9 would only not be 1 if the sender was generating invalid frames. Moreover if you are not sampling r9 the delay before it also becomes unnecessary. These lines should be removed:
delay_us(delay_time);
r9 = GPIOA->IDR;
delay_us(delay_time);
That would at least give you two bit periods where your processor could do other work other then being stuck in the interrupt context, but delaying is an interrupt handler is not good practice - it blocks execution of normal code and all lower priority interrupts making the solution unsuited to real-time systems. In this case if the soft-UART Rx is all the system has to do, you are likely to get better results by simply polling the GPIO rather then using interrupts - at least then other interrupts could run normally, and it is much simpler to implement.
Your "unrolled-loop" implementation also serves no real purpose with the delays in place - even at very high bit rates a loop overhead is likely to be insignificant over the duration of the frame, and if it were you could tweak the delays a little to compensate:
void EXTI0_IRQHandler(void)
{
delay_us(delay_time * 2 / 3);
for( int i = 7; i >= 0; i-- )
{
x |= GPIOA->IDR << i ;
delay_us(delay_time);
}
EXTI->PR |= 0X00000001;
buff1[z++] = x;
x = 0 ;
return ;
}
A more robust solution for a soft receiver that will play well with other processes in your system, should use the EXTI interrupt only to detect the start bit; the handler should disable the EXTI, and start a timer at the baud rate plus half a bit period. The interrupt handler for the timer, samples the GPIO pin at the centre of the bit period, and on the first interrupt after the EXTI, changes the period to one bit period. For each timer interrupt it samples and counts the bits until a whole data word has been shifted in, when it disables the timer and re-enables the EXTI for the next start bit.
I have successfully used this technique on STM32 running at 120MHz at 4800 and pushed it to 38400, but at 26 microseconds per bit it gets quite busy in the interrupt context, and your application presumably has other things to do?
The following is a slightly genericised version of my implementation. It uses STM32 Standard Peripheral Library calls rather then direct register access or the later STM32Cube HAL, but you can easily port it one way or the other as you need. The framing is N,8,1.
#define SOFT_RX__BAUD = 4800u ;
#define SOFT_RX_TIMER_RELOAD = 100u ;
void softRxInit( void )
{
// Enable SYSCFG clock
RCC_APB2PeriphClockCmd(RCC_APB2Periph_SYSCFG, ENABLE);
// Connect the EXTI Line to GPIO Pin
SYSCFG_EXTILineConfig( EXTI_PortSourceGPIOB, EXTI_PinSource0 );
TIM_Cmd( TIM10, DISABLE);
// NVIC initialisation
NVIC_InitTypeDef NVIC_InitStructure = {0,0,0,DISABLE};
NVIC_InitStructure.NVIC_IRQChannel = TIM1_UP_TIM10_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 12;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
// Enable peripheral clock to timers
RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM10, ENABLE);
TIM_ARRPreloadConfig( TIM10, DISABLE );
// Generate soft Rx rate clock (4800 Baud)
TIM_TimeBaseInitTypeDef init = {0};
TIM_TimeBaseStructInit( &init ) ;
init.TIM_Period = static_cast<uint32_t>( SOFT_RX_TIMER_RELOAD );
init.TIM_Prescaler = static_cast<uint16_t>( (TIM10_ClockRate() / (SOFT_RX__BAUD * SOFT_RX_TIMER_RELOAD)) - 1 );
init.TIM_ClockDivision = 0;
init.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInit( TIM10, &init ) ;
// Enable the EXTI Interrupt in the NVIC
NVIC_InitStructure.NVIC_IRQChannel = EXTI9_5_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 12;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init( &NVIC_InitStructure );
// Dummy call to handler to force initialisation
// of UART frame state machine
softRxHandler() ;
}
// Soft UART Rx START-bit interrupt handler
void EXTI0_IRQHandler()
{
// Shared interrupt, so verify that it is the correct one
if( EXTI_GetFlagStatus( EXTI_Line0 ) == SET )
{
// Clear the EXTI line pending bit.
// Same as EXTI_ClearITPendingBit( EXTI_Line11 )
EXTI_ClearFlag( EXTI_Line0 ) ;
// Call Soft UART Rx handler
softRxHandler() ;
}
}
void TIM1_UP_TIM10_IRQHandler( void )
{
// Call Soft UART Rx handler
softRxHandler() ;
TIM_ClearITPendingBit( TIM10, TIM_IT_Update );
}
// Handler for software UART Rx
inline void softRxHandler()
{
static const int START_BIT = -1 ;
static const int STOP_BIT = 8 ;
static const int HALF_BIT = SOFT_RX_TIMER_RELOAD / 2;
static const int FULL_BIT = SOFT_RX_TIMER_RELOAD ;
static int rx_bit_n = STOP_BIT ;
static const uint8_t RXDATA_MSB = 0x80 ;
static uint8_t rx_data = 0 ;
static EXTI_InitTypeDef extiInit = { EXTI_Line0,
EXTI_Mode_Interrupt,
EXTI_Trigger_Falling,
DISABLE } ;
// Switch START-bit/DATA-bit
switch( rx_bit_n )
{
case START_BIT :
{
// Stop waiting for START_BIT
extiInit.EXTI_LineCmd = DISABLE;
EXTI_Init( &extiInit );
// Enable the Interrupt
TIM_ClearITPendingBit( TIM10, TIM_IT_Update );
TIM_ITConfig( TIM10, TIM_IT_Update, ENABLE );
// Enable the timer (TIM10)
// Set time to hit centre of data LSB
TIM_SetAutoreload( TIM10, FULL_BIT + HALF_BIT ) ;
TIM_Cmd( TIM10, ENABLE );
// Next = LSB data
rx_data = 0 ;
rx_bit_n++ ;
}
break ;
// STOP_BIT is only set on first-time initialisation as a state, othewise it is
// transient within this scase.
// Use fall through and conditional test to allow
// case to handle both initialisation and UART-frame (N,8,1) restart.
case STOP_BIT :
default : // Data bits
{
TIM_ClearITPendingBit( TIM10, TIM_IT_Update );
if( rx_bit_n < STOP_BIT )
{
if( rx_bit_n == 0 )
{
// On LSB reset time to hit centre of successive bits
TIM_SetAutoreload( TIM10, FULL_BIT ) ;
}
// Shift last bit toward LSB (emulate UART shift register)
rx_data >>= 1 ;
// Read Rx bit from GPIO
if( GPIO_ReadInputDataBit( GPIOB, GPIO_Pin_0 ) != 0 )
{
rx_data |= RXDATA_MSB ;
}
// Next bit
rx_bit_n++ ;
}
// If initial state or last DATA bit sampled...
if( rx_bit_n == STOP_BIT )
{
// Stop DATA-bit sample timer
TIM_Cmd( TIM10, DISABLE );
// Wait for new START-bit
rx_bit_n = START_BIT ;
extiInit.EXTI_LineCmd = ENABLE;
EXTI_Init( &extiInit );
// Place character in Rx buffer
serialReceive( rx_data ) ;
}
}
break ;
}
}
The code works in the same way as a real UART as illustrated in the timing diagrem above with the exception that in my implementation the STOP bit is not actually sampled - it is unnecessary; it only serves to ensure that the subsequent START bit is a 1 -> 0 transition and can generally be ignored. A real UART would probably generate a framing error if it were not 1, but if you were not going to handle such errors in any event, there is no purpose in checking.

I can't see in your code where you take account of the start bit that is normally part of a serial transmission. You seem to be only looking for 8 data bits and a stop bit.
With the convention of "start bit is the inverse of stop bit" there will be an additional edge your code detects between characters, thus apparently shifting the bit stream you detect by one bit.

You mentioned that character 't' is received when string "test" is sent.
Introduce sufficient inter character delay in the string.
Hopefully it works.
You can use docklite for sending string with inter character delay.

Related

Breathing led in Tiva C series TM4C123G

I have to write a C code so that the RGB LED on the board breaths. My code is blinking not breathing. My teacher said that varying brightness is achieved by varying duty-cycle so in that case I can't use pwm. Please help me to understand this code.
#include <stdint.h>
#include <stdlib.h>
#define SYSCTL_RCGC2_R (*((volatile unsigned long *)0x400FE108))
#define SYSCTL_RCGC2_GPIOF 0x00000020 //port F clock gating control
#define GPIO_PORTF_DATA_R (*((volatile unsigned long *)0x400253FC))
#define GPIO_PORTF_DIR_R (*((volatile unsigned long *)0x40025400))
#define GPIO_PORTF_DEN_R (*((volatile unsigned long *)0x4002551C))
void delay (double sec);
int cond;
int main(void){
SYSCTL_RCGC2_R = SYSCTL_RCGC2_GPIOF;
GPIO_PORTF_DIR_R=0x0E;
GPIO_PORTF_DEN_R=0x0E;
cond=0;
while(1){
GPIO_PORTF_DATA_R = 0x02;
delay(12.5);
GPIO_PORTF_DATA_R = 0x00;
delay(0);
GPIO_PORTF_DATA_R = 0x02;
delay(2.5);
GPIO_PORTF_DATA_R = 0x00;
delay(10);
GPIO_PORTF_DATA_R = 0x02;
delay(5);
GPIO_PORTF_DATA_R = 0x00;
delay(7.5);
GPIO_PORTF_DATA_R = 0x02;
delay(7.5);
GPIO_PORTF_DATA_R = 0x00;
delay(5);
GPIO_PORTF_DATA_R = 0x02;
delay(12.5);
GPIO_PORTF_DATA_R = 0x00;
delay(0);
GPIO_PORTF_DATA_R = 0x02;
delay(7.5);
GPIO_PORTF_DATA_R = 0x00;
delay(5);
GPIO_PORTF_DATA_R = 0x02;
delay(5);
GPIO_PORTF_DATA_R = 0x00;
delay(7.5);
}
return 0;
}
void delay(double sec){
int c=1, d=1;
for(c=1;c<=sec;c++)
for(d=1;d<= 4000000;d++){}
}
There are two ways you can drive LEDs: either with constant current through some general-purpose I/O, or with repeated duty cycle from PWM. PWM meaning pulse-width modulation and it will happen with pulses that are too fast for the human eye to notice, could be anywhere from some 100Hz up to 10kHz or so.
The main advantage of PWM is that you easily can control current. Which is case of RGB means color intensity of the 3 individual LEDs. Most smaller LEDs are rated at 20mA so that's usually the maximum current you are aiming for, corresponding to 100% duty cycle.
The correct way to achieve this is to use PWM.
But what your current code does is to "bit bang" simulate PWM by pulling GPIO pins. That's very crude and inefficient. Normally microcontrollers have a timer and/or PWM hardware peripheral built in, where you just provide a duty cycle and the hardware takes care of everything from there. In this case you would set up 3 PWM hardware channels which should ideally be clocked at the same time.
LEDs are diodes with different forward voltage depending on chemistry. So you very likely have different forward voltages per each of the 3 colors. You have to check the datasheet of the RGB and look for luminous intensity experessed in candela. In this case very likely millicandela, mcd. Lets assume that your green led has 300mcd but the red and blue have 100mcd. They are somewhat linear, or you can probably get away with assuming they are. So a crude equation in this case is to give the green LED 3 times less current than the others, in order to get an even mix of colors. Once you have compensated for that, you can give your 3 PWM channels a RGB code and hopefully get the corresponding color.
As a side note, the delay function in your code is completely broken in many ways. The loop iterator for such busy-delays must be volatile or any half-decent compiler will simply remove the delay when optimizations are enabled. And there is no reason to use floating point either.
If you are doing it with your delay function and your delay resolution is in seconds as suggested in the code of course it will "blink" - the frequency needs to be faster than human visual perception - say for example about 50Hz, then to get a smooth variation you might divide that up into say 20 levels, requiring a millisecond delay.
In any case your delay() function defeats itself by taking a floating point number of seconds but comparing it with an integer loop counter - it will only ever work in whole seconds.
So given a function delayms( unsigned millisec ) (which I discuss later) then:
#define BREATHE_UPDATE_MS 100
#define BREATHE_MINIMUM 0
#define PWM_PERIOD_MS 20
unsigned tick = 0 ;
unsigned duty_cycle = 0 ;
unsigned cycle_start_tick= 0 ;
unsigned breath_update_tick = 0 ;
int breathe_dir = 1 ;
for(;;)
{
// If in PWM "mark"...
if( tick - cycle_start_tick < duty_cycle )
{
// LED on
GPIO_PORTF_DATA_R |= 0x02 ;
}
// else PWM "space"
else
{
// LED off
GPIO_PORTF_DATA_R &= ~0x02 ;
}
// Update tick counter
tick++ ;
// If PWM cycle complete, restart
if( tick - cycle_start_tick >= PWM_PERIOD_MS )
{
cycle_start_tick = tick ;
}
// If time to update duty-cycle...
if( tick - breath_update_tick > BREATHE_UPDATE_MS )
{
breath_update_tick = tick ;
duty_cycle += breathe_dir ;
if( duty_cycle >= PWM_PERIOD_MS )
{
// Breathe in
breathe_dir = -1 ;
}
else if( duty_cycle == BREATHE_MINIMUM )
{
// Breathe out
breathe_dir = 1 ;
}
}
delayms( 1 ) ;
}
Change BREATHE_UPDATE_MS to breathe faster, change BREATHE_MINIMUM to "shallow breathe" - i.e. not dim to off.
If your delay function truly results in a delay resolution in seconds then approximately and rather crudely:
void delayms( unsigned millisec )
{
for( int c = 0; c < millisec; c++ )
{
for( volatile int d = 0; d < 4000; d++ ) {}
}
}
However that suggests to me a rather low core clock rate, so you may need to adjust that. Note the use of volatile to prevent the removal of the empty loop by the optimiser. The problem with this delay is that you will need to calibrate it to the clock speed of your target and its timing is likely to differ in any case depending on what compiler you use and what compiler options you use. It is generally a poor solution.
In practice using a "busy-loop" delay for this is ill-advised and crude and it would be better to use the Cortex-M SYSTICK:
volatile uint32_t tick = 0 ;
void SysTick_Handler(void)
{
tick++ ;
}
... removing the tick and tick++ from the original; code. Then you don't need a delay in the loop above because all the timing is pegged to the value of tick. However should you want a delay for other reasons then:
delayms( uint32_t millisec )
{
uint32_t start = tick ;
while( tick - start < millisec ) ;
}
Then you would initialise the SYSTICK at start-up thus:
int main (void)
{
SysTick_Config(SystemCoreClock / 1000) ;
...
}
This assumes that you are using the CMSIS, but your code suggests that you are not doing that (or even using a vendor supplied register header). You will in that case need to get down and dirty with the SYSTICK and NVIC registers if you (or your tutor) insists on that. The source for SysTick_Config() is as follows:
__STATIC_INLINE uint32_t SysTick_Config(uint32_t ticks)
{
if ((ticks - 1UL) > SysTick_LOAD_RELOAD_Msk)
{
return (1UL); /* Reload value impossible */
}
SysTick->LOAD = (uint32_t)(ticks - 1UL); /* set reload register */
NVIC_SetPriority (SysTick_IRQn, (1UL << __NVIC_PRIO_BITS) - 1UL); /* set Priority for Systick Interrupt */
SysTick->VAL = 0UL; /* Load the SysTick Counter Value */
SysTick->CTRL = SysTick_CTRL_CLKSOURCE_Msk |
SysTick_CTRL_TICKINT_Msk |
SysTick_CTRL_ENABLE_Msk; /* Enable SysTick IRQ and SysTick Timer */
return (0UL); /* Function successful */
}

TIMER0 not executing multiple compare match register interrupt requests.(MSP430)

OK so I have been attempting to create some code using a MSP430FR5994 TI launch pad that utilizes Timer0 and 3 separate compare registers to trigger 3 separate isr's. I have successfully got one to work however as soon as I add another compare register the CCIFE flag sets and never competes the execution of the second isr. I have watched the code in the debugger on both CCstudio and IAR same thing happens in both, the set up registers are correct and the TA0R registers is counting and will trigger the first isr based on the TA0CCR0 but all other compare regs R1 2 3 etc will not trigger and execute successfully. The code is below, idea's on what I am doing wrong would be much appreciated.
#include "msp430.h"
#include <stdbool.h>
#define COUNT_1 12000
#define COUNT_2 800
int main( void )
{
// Stop watchdog timer to prevent time out reset
WDTCTL = WDTPW + WDTHOLD;
PM5CTL0 &= ~LOCKLPM5;
P1DIR |= BIT0 + BIT1;
P1OUT = BIT0 + BIT1;
//set up and enable timer A or TA0 for continous mode
TA0CCR0 = COUNT_1;
TA1CCR1 = COUNT_2;
TA0CTL = TASSEL__ACLK + MC_2; //set the max period for 16bit timer operation
TA1CTL = TASSEL__ACLK + MC_2;
TA0CCTL0 = CCIE; //enable compare reg 0
TA1CCTL1 = CCIE; //enable compare reg 1
//TA0CTL |= TAIE;
_BIS_SR( GIE); //ENABLE GLOBAL INTERRRUPTS
//set the max period for 16bit timer operation
while(true){}
}
#pragma vector= TIMER0_A0_VECTOR //compare interrupt 0 flahse red led
__interrupt void TIMER0_A0(void) {
P1OUT ^= BIT1 ;
}
#pragma vector = TIMER1_A1_VECTOR //compare interrupt 1 flashes green led
__interrupt void TIMER1_A1(void) {
P1OUT ^= BIT0;
}
The User's Guide says in section 25.2.6.1:
The TAxCCR0 CCIFG flag is automatically reset when the TAxCCR0 interrupt
request is serviced.
However, this does not happen for the other CCRx interrupts, because multiple ones use the same interrupt vector.
Section 25.2.5.2 says:
The highest-priority enabled interrupt generates a number in the TAxIV register (see register description). […]
Any access, read or write, of the TAxIV register automatically resets the highest-pending interrupt flag.
So you always have to read the TAxIV register (and with three or more CCRs, you need it to find out which CCR triggered the interrupt):
__interrupt void TIMER1_A1(void) {
switch (TA1IV) {
case TAIV__TACCR1:
P1OUT ^= BIT0;
break;
case TAIV__TACCR2:
...
break;
}
}

STM32 blink code only works when stepping on debug mode

I am trying to run a simple blink program on a STM32 board based on STM32F413RG. The led lights up and only toggles when stepping through, not when continuing without breakpoint or running freely on Release mode.
I have setup the eclipse(4.11) to debug the program using a J-link hardware debugger. The code uploads and the LED programmed lights up, but I can see it toggle only when manually stepping through. It does not run without breakpoints.
Sharing my code below where I have setup the clock to be sourced from PLL running on 32 MHz and trying to blink the LED connected to Port B pin 1 every 0.5 second.
One more interesting thing is, Even if I am able to set a breakpoint to see inside the delay() method, the debugger never stops at it / seems to jump across that line of code when single-stepping. Why does it so?
void setupClocks()
{
// we want to use the 24000000 HSE clock (xtal) as the base
RCC->CR |= RCC_CR_HSEON;
// so wait for it to be ready
while ((RCC->CR & RCC_CR_HSERDY) == 0) {}
enter code here
// now configure the PLL (HSE / 12 * 96 /6) gives 32 MHz
RCC->PLLCFGR |= RCC_PLLCFGR_PLLSRC_HSE;
RCC->PLLCFGR &= ~RCC_PLLCFGR_PLLM_Msk;
RCC->PLLCFGR |= (12)<< RCC_PLLCFGR_PLLM_Pos;
RCC->PLLCFGR &= ~RCC_PLLCFGR_PLLN_Msk;
RCC->PLLCFGR |= (96)<< RCC_PLLCFGR_PLLN_Pos; // 32 MHz
RCC->PLLCFGR &= ~RCC_PLLCFGR_PLLP_Msk;
RCC->PLLCFGR |= RCC_PLLCFGR_PLLP_1; // 6
RCC->CR |= RCC_CR_PLLON;
// wait for PLL to be ready
while ((RCC->CR & RCC_CR_PLLRDY) == 0) {}
// now setup the AHB1
// 1/2 of system clock (48 MHz)
RCC->CFGR |= RCC_CFGR_PPRE1_2;
// select PLL (clocked from HSE)
RCC->CFGR |= RCC_CFGR_SW_1;
//reset the ones we use
RCC->AHB1RSTR = RCC_AHB1RSTR_GPIOARST;
RCC->AHB1RSTR = RCC_AHB1RSTR_GPIOBRST;
RCC->AHB1RSTR = RCC_AHB1RSTR_GPIOCRST;
RCC->AHB1RSTR = RCC_AHB1RSTR_GPIODRST;
RCC->AHB1RSTR = 0;
SystemCoreClockUpdate();
}
void initLED()
{
// enable port B clock
RCC->AHB1ENR |= RCC_AHB1ENR_GPIOBEN;
// set as output for Port B pin 1
GPIOB->MODER |= GPIO_MODER_MODER1_0;
// start with MP10 LED on
GPIOB->ODR = GPIO_ODR_ODR_1;
}
void delay(uint32_t microsec)
{
// wait a bit
uint32_t counter = (microsec * (SystemCoreClock / 1000000U));
while(counter != 0U)
{
counter--;
}
}
void blinkCount(int count)
{
for (int i = 0; i < count; ++i)
{
GPIOB->ODR = ~GPIO_ODR_ODR_1 ;
delay(500000);
GPIOB->ODR = GPIO_ODR_ODR_1;
delay(500000);
}
delay(1000000);
}
int main()
{
setupClocks();
initLED();
while(1)
{
blinkCount(1);
delay(1000000);
}
return 0;
}
Expecting to blink the led as per desired frequency when the program is run without breakpoints or release mode, but the led activity is only visible when stepping through during debug mode.
Be consistent. If you set the clock using directly registers, do not call HAL cube generated functions like SystemCoreClockUpdate(); it is very likely the SystemCoreClock will not have the value you think it has
When doing blocking delays I advice using volatile variables as they will not be removed by the compiler. There is no need of using the 64 bits variables unless you want many minutes delays. Try to do not block. use SysTick (or any other timer) interrupt to implement delays.
example
void delay(volatile uint32_t delay)
{
while(delay--);
}
or for more precise control inline assembly:
void delay1(uint32_t delay)
{
while(delay--)
{
asm volatile("" : : "r"(delay) : "memory");
}
}
which leads to the code:
delay:
sub sp, sp, #8
str r0, [sp, #4]
.L2:
ldr r3, [sp, #4]
sub r2, r3, #1
cmp r3, #0
str r2, [sp, #4]
bne .L2
add sp, sp, #8
bx lr
delay1:
.L6:
subs r0, r0, #1
bxcc lr
b .L6
You need a small delay after enabling the GPIO clock. Put a __DSB() call between enabling the clock and accessing the GPIO registers.
See the product errata document for details.

DRDY PIC18F45K80 to QT1481

I also have a problem with DRDY. I need to include DRDY. The pins for DRDY are RD2 and RD5. They are both inputs.
Here is the information for DRDY.
DRDY Pin
DRDY is an open-drain output (in SPI mode) or bidirectional pin (in UART mode) with an internal 20 k – 50 k pullup
resistor.
Most communications failures are the result of failure to properly observe the DRDY timing.
Serial communications pacing is controlled by this pin. Use of DRDY is critical to successful communications with the
QT1481. In either UART or SPI mode, the host is permitted to perform a data transfer only when DRDY has returned
high. Additionally, in UART mode, the QT1481 delays responses to the host if DRDY is being held low by the host.
After each byte transfer, DRDY goes low after a short delay and remains low until the QT1481 is ready for another
transfer. A short delay occurs before DRDY is driven low because the QT1481 may otherwise be busy and requires
a finite time to respond.
DRDY may go low for a microsecond only. During the period from the end of one transfer until DRDY goes low and
back high again, the host should not perform another transfer. Therefore, before each byte transmission the host
should first check that DRDY is high again.
If the host wants to perform a byte transfer with the QT1481 it should behave as follows:
1. Wait at least 100 µs after the previous transfer (time S5 in Figure 3-2 on page 23: DRDY is guaranteed to go
low before this 100 µs expires).
2. Wait until DRDY is high (it may already be high).
3. Perform the next transfer with the QT1481.
In most cases it takes up to 3 ms for DRDY to return high again. However, this time is longer with some commands
or if the STS_DEBUG setup is enabled, as follows:
0x01 (Setups load): <20 ms
0x02 (Low Level Cal and Offset): <20 ms
Add 15 ms to the above times if the STS_DEBUG setup is enabled.
Other DRDY specifications:
Min time DRDY is low: 1 µs
Max time DRDY is low after reset: 100 ms
The timing diagram is this:
How can implement that?
The code I have written with my friend is written here:
#include <xc.h>
#include "PIC.h"
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
//#include <pic18f45k80.h>
#define MSB 1
#define LSB 0
// SPI PIN CONFIGURATION
#define SCK_TRIS TRISCbits.TRISC3 = 0 ;
#define SDO_TRIS TRISCbits.TRISC5 = 0 ;
#define SDI_TRIS TRISCbits.TRISC4 = 1 ;
#define QTA_SS_TRIS TRISDbits.TRISD4 = 0 ;
#define QTB_SS_TRIS TRISEbits.TRISE2 = 0 ;
#define QTA_SS_LAT_LOW LATDbits.LATD4 = 0 ;
#define QTA_SS_LAT_HIGH LATDbits.LATD4 = 1 ;
#define QTB_SS_LAT_LOW LATEbits.LATE2 = 0 ;
#define QTB_SS_LAT_HIGH LATEbits.LATE2 = 1 ;
#define QTA_DRDY_TRIS TRISDbits.TRISD5 = 1 ;
#define QTB_DRDY_TRIS TRISDbits.TRISD2 = 1 ;
#define QTA_DRDY_LAT_LOW LATDbits.LATD5 = 0 ;
#define QTA_DRDY_LAT_HIGH LATDbits.LAT52 = 1 ;
#define QTB_DRDY_LAT_LOW LATDbits.LAT25 = 0 ;
#define QTB_DRDY_LAT_HIGH LATDbits.LATD2 = 1 ;
#define QTB_DRDY PORTDbits.RD2 ;
#define QTA_DRDY PORTDbits.RD5 ;
// FREQUENCY SELECT
#define _XTAL_FREQ 16000000
// PIN SETUP
void PIN_MANAGER_Initialize(void)
{
/**
LATx registers
*/
LATE = 0x00;
LATD = 0x00;
LATA = 0x00;
LATB = 0b00010000;
LATC = 0x00;
/**
TRISx registers
*/
TRISE = 0x00;
TRISA = 0x08;
TRISB = 0x01;
TRISC = 0b00010000;
TRISD = 0xEF;
PORTC = 0b00010010 ;
/**
ANSELx registers
*/
ANCON0 = 0x00;
ANCON1 = 0x00;
/**
WPUx registers
*/
WPUB = 0x00;
INTCON2bits.nRBPU = 1;
}
// SPI
void SPI_Initialize(void)
{
// SMP Middle; CKE Idle to Active;
SSPSTAT = 0b00000000;
// SSPEN enabled; WCOL no_collision; CKP Idle:High, Active:Low; SSPM FOSC/4; SSPOV no_overflow;
SSPCON1 = 0b00111010;
// SSPADD 0;
SSPADD = 0x00;
ADCON0 = 0 ;
ADCON1 = 0x0F ; //Makes all I/O digital
SCK_TRIS ;
SDO_TRIS ;
SDI_TRIS ;
QTA_SS_TRIS ;
QTB_SS_TRIS ;
QTA_DRDY_TRIS ;
QTB_DRDY_TRIS ;
}
signed char WriteSPI( unsigned char data_out )
{
unsigned char TempVar;
TempVar = SSPBUF; // Clears BF
PIR1bits.SSPIF = 0; // Clear interrupt flag
SSPCON1bits.WCOL = 0; //Clear any previous write collision
SSPBUF = data_out; // write byte to SSPBUF register
if ( SSPCON1 & 0x80 ) // test if write collision occurred
return ( -1 ); // if WCOL bit is set return negative #
else
while( !PIR1bits.SSPIF ); // wait until bus cycle complete
return ( 0 ); // if WCOL bit is not set return non-negative#
}
unsigned char ReadSPI( void )
{
unsigned char TempVar;
TempVar = SSPBUF; // Clear BF
PIR1bits.SSPIF = 0; // Clear interrupt flag
SSPBUF = 0x00; // initiate bus cycle
while(!PIR1bits.SSPIF); // wait until cycle complete
return ( SSPBUF ); // return with byte read
}
unsigned char DataRdySPI( void )
{
if ( SSPSTATbits.BF )
return ( +1 ); // data in SSPBUF register
else
return ( 0 ); // no data in SSPBUF register
}
// SOFTWARE EUART
void out_char(char character, char bit_order){
uint8_t i = 0;
RSOUT = 1 ; // MSB
__delay_ms(1);
RSOUT = 0 ; // START
__delay_us(100);
for (i = 8; i>0; --i){
if (bit_order){ // Bit order determines how you will put the bits, from left to right (MSB) or right to left (LSB)
RSOUT = (character & 0x80) ? 1:0; // in MSB you compare the left-most bit doing an AND with 0x80, and put 1 if true, 0 elsewhere.
character <<= 1; // Shift the character to the left, discrading the bit just sent
} else {
RSOUT = (character & 0x01); // in LSB you compare the right-most bit doing an AND with 0x01, and put 1 if true, 0 else.
character >>= 1; // Shift the character to the right, discrading the bit just sent
}
__delay_us(100);
}
RSOUT = 1 ; // STOP
}
void out_str(char * string, uint8_t len, char bit_order){
uint8_t i = 0;
for (i = 0; i< len; i++){
out_char(string[i], bit_order);
}
}
void SYSTEM_Initialize(void)
{
PIN_MANAGER_Initialize() ;
SPI_Initialize() ;
}
void main(void)
{
SYSTEM_Initialize() ;
while (1)
{
QTB_SS_LAT_LOW ; // Transmit data
char temp ;
WriteSPI(0x0F) ; // Send a byte
while(!DataRdySPI()) ; // wait for a data to arrive
temp = ReadSPI(); // Read a byte from the
QTB_SS_LAT_HIGH ; // Stop transmitting data
__delay_us(100) ;
}
}
No. Do not just write a bunch of code, then see what it does. That kind of shotgun (or, if you prefer, spaghetti-to-the-wall) approach is a waste of effort.
First, drop all those macros. Instead, write comments that describe the purpose of each chunk of code, like the first three assignments in your SPI_Initialize() function.
Next, convert your specification to pseudocode. The format does not matter much, just use something that lets you keep your mind focused on what the purpose is, rather than on the details on how to do it.
The datasheet says that with SPI, there are three outputs from the PIC (^SS, SCK, MOSI on the QT1481), and two inputs (^DRDY and MISO on the QT1481). I'll use those names for the data lines, and for their respective I/O pin names on the PIC.
The setup phase on the PIC should be simple:
Make ^DRDY an input
Make ^SS an output, set it HIGH
Make SCK an output, set it LOW
Make MOSI an output, set it LOW
Make MISO an input
Set up SPI using SCK, MOSI, MISO
Each transfer is a bidirectional one. Whenever you send data, you also receive data. The zero command is reserved for receiving multiple data, says the datasheet. So, you only need a function that sends a byte, and at the same time receives a byte:
Function SPITransfer(command):
Make sure at least 0.1ms has passed since the previous transfer.
Do:
Nothing
While (^DRDY is LOW)
Set ^SS LOW
response = Transfer(command)
Set ^SS HIGH
Return response
End Function
As I understand it, for PICs and properly initialized hardware SPI the response = Transfer(command) line is in C
SSPBUF = command;
while (!DataRdySPI())
;
response = SSPBUF;
You can also bit-bang it, in which case it is (in pseudocode):
response = 0
For bit = 7 down to 0, inclusive:
If (command & 128):
Set MOSI high
Else:
Set MOSI low
End If
Set SCK low
Sleep for a half period
command = command / 2
response = response * 2
If MISO high:
response = response + 1
End If
Set SCK high
Sleep for a half period
End For
but obviously the hardware SPI approach is better.
(When you get this working, you can use the hardware SPI without a wait loop from a timer interrupt, making the communications essentially transparent to the "main operation" of the PIC microcontroller. That requires a slightly different approach, with a command and response queues (of a few bytes), but will make it much easier for the PIC to do actual work, other than just scan the QT1481.)
After a reset, you essentially send 0x0F until you get 0xF0 back:
while (SPITransfer(0x0F) != 0xF0)
;
At this point, you have the steps you need to implement in C. OP also has the hardware (an oscilloscope) to verify their code works.

MSP 430G2553 Sampling in Compare Mode with Timer_A

Friends , I have to sample the input , every 14 microsecond in a 61 micro second slot with the timer input (Project Requirement).
I have to do it 8 times to make a byte. More like UART , but I am using it for One Wire Bus communication for my Masters Project.
I have written the code as below , which gives expected result, tested it executing one instruction at a time in debugger.
Below is the Code.
/*****************************************************************************
COMPARE MODE SAMPLING:
MCLK and SCLK #8MZ
The code configures P2.1 as TA1.CCI1A input.
It samples the input at P2.1 Whenever the TA1R reaches the TA1CCR1 value.
It samples input on P2.1 every 14us once in a duration of 61 us.
It then reads 8 bits one by one to read a byte.
******************************************************************************/
#include "io430g2553.h"
#define MSP_DQ BIT5
unsigned char word=0x00;
unsigned char i=0;
unsigned char temp;
void Read(void)
{
TA1CCR0 = 0x1E8; // 61 micro secs
TA1CCR1 = 0x70; // 14 micro secs
//TA0CCTL1 = CM_2 | CCIS_0 | SCS | CAP | OUTMOD_0 | CCIE;
//Keep in mind that It should not be configured as campture mode
TA1CCTL1 |= CM_2 | CCIS_0 | SCS | OUTMOD_0 | CCIE;
TA1CTL = TASSEL_2 + MC_1 + ID_0; // Register TA0CTL -> SMCLK/1, Up mode
do{
while ((TA1CCTL0 & CCIFG) == 0 ) // wait while CCIF is set
{
}
**TA1CCTL0 &= ~CCIFG; // Clear the flag** (%1%)
//TA1CTL &= ~TAIFG; // Clear the flag
i++;
} while( i<8) ;
TA1CTL = TACLR; // Stop the Timer
TA1CCTL1 = 0x00;
}
void Configure_CCI1A(void)
{
// Configuring P2.1 as TA1.CCI1A
P2OUT &= 0x00; // Clearing P1OUT
P2DIR &= ~BIT1 ; // Configuring P1.2 as input
P2SEL |= BIT1 ; // P2.1 Timer1_A, capture: CCI1A input, compare: Out1 output
P2SEL2 &= ~BIT1 ;
}
void main(void)
{
WDTCTL = WDTPW + WDTHOLD; // Stop WDT
BCSCTL1 = CALBC1_8MHZ;
DCOCTL = CALDCO_8MHZ;
P1OUT &= 0x00; // Clearing P1OUT
P1DIR |= BIT0 ; // Configuring P1.0 as Output
__enable_interrupt();
Configure_CCI1A();
Read();
**P1OUT ^= BIT0;** //(%2%)
while(1) {
}
}
// Timer A1 interrupt service routine
#pragma vector=TIMER1_A1_VECTOR
__interrupt void Timer1_A1 (void)
{
P1OUT ^= BIT0; // To show Read occured
word <<=1; // If x = 00000010 (binary) => x <<= 2; => x=00001000
temp=0x00;
temp=((TA1CCTL1 & SCCI)>>10);
**word = word + temp ;** //(%3%)
}
But the issue is that when I call the function it some how appears stuck . I guess it is not coming out of the ISR cleanly though it completed all its execution when I run in debugger one instruction at a time. To make my question clear , This is how I tested :
Suppose if I put toggle break point at highlighted expression (%3%) in ISR , then it enters the ISR hits the togglebreak 8 times capturing correct values and comes out of Read Function neatly ( and also
while ((TA1CCTL0 & CCIFG) == 0 ) // wait while CCIF is set
{
}
and
{
....
....
i++;
} while( i<8) ;
out of above loop)
, to reach while(1) expression in the mains.
But instead if i put toggle point at the highlighted expression (%1%) it appears stuck.
OR if I put the toggle break point directly at (%2%) in main what i expect , it to complete the Read function , store the value in word variable to reach the toggle break point but the code appears stuck and does not hit the togglebreak.
I do not know what is wrong, can any one please help ?
The interrupt flag is automatically cleared when you read TAIV or if you manually clear it in TACCTL1. However, you do neither of these things in your ISR so the interrupt remains pending and your CPU continually executes your ISR and no other code so it never gets a chance to exit Read().
My guess is that by putting a breakpoint in the ISR your development environment causes a read from TAIV and clears the pending interrupt. I've experienced that before, but not sure how common that behaviour is as it is undesirable.

Resources