Question about Peripheral Timing Generator (PTG) Module on dsPIC33CK256MP508 - c

I am a newbie to programming in the Microchip PIC environment, so please excuse my naivete! I recently began experimenting with the dsPIC33CK Curiosity dev board (which contains the dsPIC33CK256MP508 dsp/mcu at its core), and have been exploring the on-board modules, like direct memory access (DMA) and the peripheral trigger generator (PTG).
DMA is working, but I am having trouble with a seemingly-simple PTG task, and I was wondering if anyone has any ideas -- the task involves a simple external interrupt/trigger to the PTG, which then needs to trigger another external pin to go logic-high.
In real life, an incoming logic-high signal will indicate the start of a series of analog data to be read, so the outgoing signal will (repeatedly) trigger an external ADC. I am using an external ADC (an ADS831, which I have working well) since the internal ADC in the dsPIC33 does not sample at the rates I need.
I am unsure if my mistake lies with my PTG commands, or if maybe my Peripheral Pin Select (PPS) module setup is flawed. Unfortunately, it appears that the builtin IO pin unlocking function ("__builtin_write_RPCON()") is undefined, at least with my included libraries. So, fell back on in-line assembler to perform the unlock sequence (detailed in datasheet DS70005349J-page 132).
Also strange: you'll notice I start my PTG sequence on "STEP3", though the PTG documentation (e.g., DS70000669B-page 33) advises starting at "STEP0". The reason I've done this is because "STEP1" and "STEP2" are apparently used by more than one Special Function Register (SFR). I discovered this in a support file included in my compiler directory (specifically, lines 26278 and 26281 of file "p33CK256MP508.h").
Anyway, here is my simple PTG-testbed code:
/* Testbed to explore the PTG module.
*
* File: newmain.c
* Author: benjamin sadler
*
* Created on January 26, 2023, 11:43 AM
*/
#include <xc.h>
/*
#define PTGCTRL(x) ((0x0 << 4) | ((x) & 0x0F)) // PTG command definitions
#define PTGWHI(x) ((0x4 << 4) | ((x) & 0x0F)) // from DS70000669B-page 15
#define PTGTRIG(x) ((0x4 << 5) | ((x) & 0x1F)) // (currently commented to
#define PTGJMPC0(x) ((0x6 << 5) | ((x) & 0x1F)) // try alternate commands
*/ // (see lines 58-60))
void init_PTG(void); // init function primitives
void init_PPS(void);
int main(int argc, char** argv) {
ANSELB = 0x0; // configure PORTB and PORTC
ANSELC = 0x0; // as digital, and set
TRISBbits.TRISB15 = 0; // direction bits:
TRISCbits.TRISC0 = 1; // B15->out, C0->in
init_PPS(); // initialize PPS
init_PTG(); // initialize PTG
PTGCSTbits.PTGSTRT = 1; // start PTG ...
while(1); // .. and wait for INT2->high.
return (1);
}
void init_PPS( void )
{
INTCON2bits.GIE = 1; // enable global interrupts
asm ("mov #0x55, w0"); // "__builtin_write_RPCON()"
asm ("mov w0, _NVMKEY"); // function doesn't appear
asm ("mov #0xAA, w0"); // to be defined, so in-line
asm ("mov w0, _NVMKEY"); // assembly needed unlock IOLOCK
RPCONbits.IOLOCK = 0; // (that is, set IOLOCK = 0)
_INT2R = 48; // PPS connect INT2 with RP48/PORTC0
asm ("mov #0x55, w0"); // more in-line assembly to
asm ("mov w0, _NVMKEY"); // re-lock IOLOCK ...
asm ("mov #0xAA, w0");
asm ("mov w0, _NVMKEY");
RPCONbits.IOLOCK = 1; // re-lock IOLOCK
}
void init_PTG( void )
{
PTGCON = 0; // most bits on PTGCST and
PTGCST = 0; // PTGCST registers should be
PTGCSTbits.PTGEN = 1; // zero, except PTGEN=1 which
// enables module
PTGQPTR = 3; // start at STEP3 (STEP1, STEP2
// aren't available??)
PTGC0LIM = 5; // loop 5 times back to STEP3
/* // set STEP commands:
_STEP3 = PTGWHI(15); // wait for INT2 interrupt
_STEP4 = PTGTRIG(25); // then trigger RP47/B14 high
_STEP5 = PTGJMPC0(3); // and jump back to STEP3
*/
_STEP3 = 0b01001111; // alternate try at setting
_STEP4 = 0b10011001; // PTG step commands, using
_STEP5 = 0b11000011; // data from Table 24-1 in
} // datasheet DS70005349J-page 478
And here are the results I'm getting:
Using the simulator (MPLAB X IDE v6.05, with xc16 compiler v2.00) with the correct device (dsPIC33CK256MP508) selected, this code will compile and run, but when I feed in a simulated RC0-> high with the Stimulus tool, I can see that PORTC0 goes high in the variable watch window, but the PTG-triggered output (on RP47/PORTB15) is never observed.
B15 stays low :(
You can also see I've tried programming the PTG steps both using the suggested bit-wise operators (currently commented out), as well as manually loading the bit fields.
I have worked on this for several days without any success, and now I humbly ask for help!

Related

freeRTOS doesn't work properly on atmega32A

I am new to Free RTOS, and I was following some tutorial line by line but things didn't sum up correctly, I used free RTOS to toggle 3 LEDS but it lights just 2 of them without toggling! random 2 LEDs, whatever I change the priorities or the delay time of toggling. random 2 LEDs just switch on and nothing more, I tried the code on proteus simulation and on real hardware and the same problem exists. can someone help me with this?
M/C: ATMEGA32A
RTOS: FreeRTOS
#define F_CPU 1000000UL
#include <avr/io.h>
#include <util/delay.h>
/* FreeRTOS files. */
#include "FreeRTOS.h"
#include "task.h"
#include "croutine.h"
#include "FreeRTOSConfig.h"
/* Define all the tasks */
static void ledBlinkingtask1(void* pvParameters);
static void ledBlinkingtask2(void* pvParameters);
static void ledBlinkingtask3(void* pvParameters);
int main(void) {
/* Call FreeRTOS APIs to create tasks, all tasks has the same priority "1" with the
same stack size*/
xTaskCreate( ledBlinkingtask1,"LED1",
configMINIMAL_STACK_SIZE, NULL, 1, NULL );
xTaskCreate( ledBlinkingtask2,"LED2",
configMINIMAL_STACK_SIZE, NULL,1, NULL );
xTaskCreate( ledBlinkingtask3,"LED3",
configMINIMAL_STACK_SIZE, NULL,1, NULL );
// Start the RTOS kernel
vTaskStartScheduler();
/* Do nothing here and just run infinte loop */
while(1){};
return 0;
}
static void ledBlinkingtask1(void* pvParameters){
/* Define all variables related to ledBlinkingtask1*/
const uint8_t blinkDelay = 100 ;
/* make PB0 work as output*/
DDRB |= (1<<0); //PB0
/* Start the infinte task 1 loop */
while (1)
{
PORTB ^= (1<<0); //toggle PB0 //PB0
vTaskDelay(blinkDelay); //wait some time
}
}
static void ledBlinkingtask2(void* pvParameters){
/* Define all variables related to ledBlinkingtask2*/
const uint8_t blinkDelay = 100;
/* make PB1 work as output*/
DDRB |= (1<<1);//PB0
/* Start the infinte task 2 loop */
while (1)
{
PORTB ^= (1<<1); //toggle PB0 //PB0
vTaskDelay(blinkDelay); //wait some time
}
}
static void ledBlinkingtask3(void* pvParameters){
/* Define all variables related to ledBlinkingtask3*/
const uint16_t blinkDelay = 100;
/* make PB2 work as output*/
DDRB |= (1<<2); //PB2
/* Start the infinte task 3 loop */
while (1)
{
PORTB ^= (1<<2); //toggle PB0 //PB0
vTaskDelay(blinkDelay); //wait some time
}
}
ps: every task works well alone but not together!
As already mentioned in comments - the major problem seems to be that access to the port register driving the LEDs is neither
PORTB ^= (1<<0); // in task 1
[...]
PORTB ^= (1<<1); // in task 2
[...]
PORTB ^= (1<<2); // in task 3
atomic
protected (by disabling interrupts during access, or by RTOS measures such as a mutex)
deployed to one unique task:
It may be misleading that the access to HW register is performed using a single instruction in the C code every time.
Still, this doesn't help because the compiler generates several assembler instructions (e.g., load previous port value to register, modify that register value, write it back to the port). This way, one task can interrupt another between those assembler/CPU instructions and modify the intermediate value.
Several tasks writing back "their" register value to the port in turn can revert what other task(s) may have just written to the port, so you miss a blinky event (or several, if this happens systematically).
The solution is therefore to protect the assignments against each other.
In the same order as numbered above, this may mean either of the following:
Check if the hardware offers a "set value" or "reset value" register beside the main PORTB port register. If so, writing a single bit to that port would be an atomic way to have the LED toggle.
I'm sorry that I don't know the hardware interface of Atmega. Maybe, this isn't possible, and you have to go on directly to 2. and 3.
a. Disable interrupts before changing the port register, reenable it afterwards. This way, the task scheduler won't run during that period (= critical section) and nobody disturbs the task that accesses the hardware.
b. Use taskENTER_CRITICAL()/taskEXIT_CRITICAL()
c. Use a mutex or similar.
Create a fourth task which waits (blocking) at a mailbox/queue.
Whenever it receives a value from the mailbox, it processes it (e.g., by XOR-ing it to the port register).
The three existing tasks don't access the LED port register themselves, but instead send such a value (= request message) to the new task.
Assign a higher priority to the new task in order to get a smooth blinking pattern.
If option 1. is possible on your controller, it is fastest (but it requires certain features in the hardware platform...). Otherwise, I agree with the hint from #Richard, option 2.b. are fastest (2.a. is as fast, but not as clean because you break the layering of the FreeRTOS lib).
Option 2.c. may introduce a notable overhead, and option 3. is very clean but a complete overkill in your situation: If your question is really only about blinking LEDs, please leave the bulldozer inside the garage and choose option 2.

I need to know why my interrupts are not working here

I am using stm8l - discovery and i have created a code for toggling a led for every 1 second using timer (TIM1) but this is not working properly. I am missing something here in my configuration
I could enter the interrupt function for the first time but after that it does not enter the interrupt function. Someone please look into and help me out
enter code here
#include <iostm8l.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "defs.h"
unsigned int count = 0;
#svlreg #interrupt void TIM1(void)
{
count += 1;
TIM1_SR1 &= ~(0x01);
}
main()
{
CLK_DIVR = 0x00; // Set the frequency to 16Mhz
CLK_PCKENR2 = 0x02; // clock for timer1
PC_DDR = 0x80; // direction output for led
PC_CR1 = 0x80; // fast push pull mode
PE_DDR = 0x80; // direction output for led
PE_CR1 = 0x80; // fast push pull mode
TIM1_PSCRH = 0x3e; //to create a frequency for 1000 hz
TIM1_PSCRL = 0x80; // so prescalar is 16000
TIM1_CR1 = 0x01;
TIM1_IER = 0x01;
_asm("rim\n");
while(1)
{
if (count == 1000)
{
PE_ODR ^= 0x80;
count = 0;
}
}
}
The interrupt enters only one time but after that it does not enter. So variable "count" remains at value 1
You are using magic numbers for the bit masks instead of defined constants, so the code is pretty damn hard to read for you and me both. Change this so that the code ends up like for example
TIM1_SR1 &= ~TIM1_SR1_UIF;
Since this is a 8 bit MCU it is also absolutely essential that you u suffix all integer contants, or they will be of type signed int, which you don't want.
For example this code TIM1_SR1 &= ~(0x01); is equivalent to TIM1_SR1 &= -2. Very easy to write accidental subtle bugs this way. I recommend studying Implicit type promotion rules.
It is highly recommended to disassemble every ISR you write to see what machine code you actually end up with, and single step it through the debugger while watching the register as well. This particular register seems to ignore having 1 written to it, so you could probably just do TIM1_SR = TIM1_SR1_UIF;. Incorrectly cleared timer flags inside ISRs is one of the most common bugs in embedded systems.
Quoting the manual:
UIF: Update interrupt flag
– At overflow or underflow if UDIS = 0 in the TIM1_CR1 register
– When CNT is re-initialized by software using the UG bit in TIM1_EGR register, if URS = 0 and UDIS = 0 in the TIM1_CR1 register.
– When CNT is re-initialized by a trigger event (refer to the TIM1_SMCR register description), if URS = 0 and UDIS = 0 in the TIM1_CR1 register
Your code doesn't appear to do any of this, so it is pretty safe to assume the timer counter isn't reset.
Another problem is that count must be declared as volatile or otherwise the compiler might optimize out this code completely:
if (count == 1000)
{
PE_ODR ^= 0x80;
count = 0;
}

How to implement interrupt flags in C, using a Microcontroller?

I have set up a project using a 16-bit Microcontroller called Thunderbird12, similar to the 9s12/Freescale 68HC12 family. It does a few things but mainly turn on an external water pump. Everything works fine except I need to implement an interrupt. I want to be able to stop the motor using an interrupt through a push button.
I have set up the button which sets pin 0 of port P to high when pressed. Using C, I have initialized the hardware and written the code but the flag is not called. Please see code below.
// Interrupt function
int interruptFlag;
void interrupt 56 WaterPumpRoutine() {
if ((PIFP & 0x01) == 0x01) { // check if pin 0 of port p is high (when button is pressed)
interruptFlag = 1; // set the flag to 1
}
// Main
void main() {
DDRP = 0x00; // set port P as input
PIEP = PIEP | 0x01; // enable interrupts on port P, pin 0
PERP = PERP | 0x01; // enable pull-up/down on port P, pin 0
if ( interruptFlag == 1)
PORTB = (PORTB & 0x00) // Here I'm turning off all the pins in Port B, which includes the pump.
}
If I put this code PORTB = (PORTB & 0x00) inside the WaterPumpRoutine() function, it works fine, but I need to be able to call the flag anywhere. I'm not sure what I'm missing. Any help would be appreciated!
Assuming that the interrupt call works fine..
Change the variable int interruptFlag; to volatile int interruptFlag;. This is to avoid optimizing away the if condition in main by the compiler.
In main you need to reset the interruptFlag on some condition. This is program dependent. Maybe you can do it like this.
if ( interruptFlag == 1)
{
PORTB = (PORTB & 0x00) // Here I'm turning off all the pins in Port B, which includes the pump.
interruptFlag = 0;
}
If you are using a push button, you should introduce a debounce mechanism to avoid detecting multiple inputs.
You need to add the if condition in main inside a while(1) loop as the interrupt can come at any time.
while(1)
{
if(interruptFlag == 1)
{
...
}
}
If u still can't get interrupt to fire , after things that other suggested, you should check if you got global interrupts or interrupt for specific part (GPIO) enabled since in most uc setting GPIO to generate interrupts doesn't enable interrupts

Interrupt-driven USART using UDRE with ATmega328P

I am having a problem with programming the serial for Arduino in C. The application demands speed, so I need to do it in C. I am using Codeblocks for it as it is easy to compile.
As I also want the serial feature not blocking the application and giving debug capability, I am trying to write a circular buffer type of serial library. The application does not need to receive data, only print.
The problem begins with the serial interrupt apparently not firing and the program blocks, the compiler does give a warning:
main.c|11|warning: ‘UART_UDRE_vect’ appears to be a misspelled signal handler|
The test program is as follows:
#include<avr/io.h>
#include<avr/interrupt.h>
#define USART_BAUDRATE 9600
#define BAUD_PRESCALE (((F_CPU/(USART_BAUDRATE*16UL)))-1)
char ok = 0;
ISR(UART_UDRE_vect) {
ok = 1;
UCSR0B &= ~(1<<5);
}
int main(void) {
UBRR0H = (BAUD_PRESCALE >> 8);
UBRR0L = BAUD_PRESCALE;
UCSR0B |= (1<<TXEN0);
UCSR0C |= (1<<UCSZ00) | (1<<UCSZ01);
sei();
while(1){
// write the byte to the serial port
UDR0 = '0';
UCSR0B |= (1<<5);
while(ok != 1){}
ok = 0;
UDR0 = '\n';
UCSR0B |= (1<<5);
while(ok != 1){}
ok = 0;
}
return 0;
}
The configuration and baud rates are correct because the echo example found here does work.
Also other examples does not use UDRE, only RX interruption and that is not what I am looking for.
Am I forgetting something?
Two problems:
According to the documentation you linked, the interrupt vector for your particular part ATmega328p should be named USART_UDRE_vect.
Variables shared with an interrupt must always be declared as volatile or the compiler might not understand that they are used and therefore break your program upon optimization. Basically the compiler thinks that the variable ok can never get another value than 0, since it doesn't see a function call to the ISR anywhere (since it is called by hardware, not by the program). Change the declaration to static volatile char ok;

External Interrupt Setup on STM32L1 doesn't run ISR

for 4 days now, I am struggling to set up External interrupt on my STM32 and I have gone through tons of reading and other people's code to get it. But no luck.
I have two buttons and when pressing either one of them I expect to light up an LED, this example is only to get it working, I wanted to have something functional before proceeding and building rest of the code.
I am sorry if the code is a little messy, but I am working on neating my coding skills.
I've gone through manuals and datasheets but nothing seems to help.
Here is my main.c
#include "stm32l1xx_hal.h"
#include "buttons.h"
static void MX_GPIO_Init(void);
bool RightButtonFlag = 0;
bool LeftButtonFlag = 0;
int main(void)
{
HAL_Init();
SystemClock_Config();
controls_Interrupt_Init();
MX_GPIO_Init();
while (1)
{
if(RightButtonFlag){
Blue_ON();
}
if(LeftButtonFlag){
Green_ON();
}
}
}
static void MX_GPIO_Init(void)
{
GPIO_InitTypeDef GPIO_InitStruct;
/* GPIO Ports Clock Enable */
__HAL_RCC_GPIOC_CLK_ENABLE();
GPIO_InitStruct.Pin = GPIO_PIN_10|GPIO_PIN_11;
GPIO_InitStruct.Mode = GPIO_MODE_IT_FALLING;
GPIO_InitStruct.Pull = GPIO_PULLUP;
HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);
{
Followed by part of stm32l1xx_it.c
#include "stm32l1xx_hal.h"
#include "stm32l1xx.h"
#include "stm32l1xx_it.h"
#include "buttons.h"
extern volatile uint8_t RightButtonFlag;
extern volatile uint8_t RightButtonFlag;
void EXTI15_10_IRQHandler(void)
{
if(GPIOC->IDR & GPIO_IDR_IDR_10){
RightButtonFlag = 1;
EXTI->PR |= EXTI_PR_PR10;
}
if(GPIOC->IDR & GPIO_IDR_IDR_11){
LeftButtonFlag = 1;
EXTI->PR |= EXTI_PR_PR11;
}
}
buttons.c
void controls_Interrupt_Init(void){
RCC->APB2ENR |= RCC_APB2ENR_SYSCFGEN; /* Enable System Configuration Register */
SYSCFG->EXTICR[3] |= SYSCFG_EXTICR3_EXTI11_PC; /* Set up External Interrupt for Pin 11 Port C */
SYSCFG->EXTICR[3] |= SYSCFG_EXTICR3_EXTI10_PC; /* Set up External Interrupt for Pin 10 Port C */
EXTI->IMR |= EXTI_IMR_MR11;
EXTI->IMR |= EXTI_IMR_MR10;
EXTI->FTSR |= EXTI_FTSR_TR11; /* Falling trigger Selection Reg. Trigger 11 */
EXTI->FTSR |= EXTI_FTSR_TR10; /* Falling trigger Selection Reg. Trigger 10 */
NVIC_SetPriority(EXTI15_10_IRQn,1); /* Set Interrupt priority for pins 10-15 */
NVIC_EnableIRQ(EXTI15_10_IRQn); /* Enable NVIC for Pins Between 10-15 */
}
and buttons.h
void controls_Interrupt_Init(void);
#define Blue_ON() (HAL_GPIO_WritePin(BLUE_LED_PORT, BLUE_LED_PIN, 1))
#define Green_ON() (HAL_GPIO_WritePin(GREEN_LED_PORT, GREEN_LED_PIN, 1))
I am fairly new to coding and with my poor experience I expect to have screwed up something very simple.
You have to declare the flags as volatile. That's a hint to the compiler that the variable can change any time, independent of the normal program flow.
volatile bool RightButtonFlag = 0;
volatile bool LeftButtonFlag = 0;
When there is no volatile there, the compiler can assume that the flags never change in the main loop, and optimize it that way, that the variables will be loaded only once, before the loop.
Declaring them volatile only in the scope of the interrupt handler does no good, because the optimization will still take place in main(), where the volatile declaration is not visible.
It is good practice to move the declaration to a header file, and include that header everywhere the variable is referenced. Then the compiler can check that the types are indeed compatible.
UPDATE
The interrupt handler makes little sense. You set EXTI to detect a falling edge, then check if the input is high. Checking the GPIO data register is not reliable anyway, due to the bouncing effect of mechanical buttons.
You should rather check EXTI->PR in the handler, and reset the pending bit with a simple assignment instead of |=, otherwise you could accidentally clear another pending bit too.
void EXTI15_10_IRQHandler(void)
{
if(EXTI->PR & EXTI_PR_PR10){
RightButtonFlag = 1;
EXTI->PR = EXTI_PR_PR10;
}
if(EXTI->PR & EXTI_PR_11){
LeftButtonFlag = 1;
EXTI->PR = EXTI_PR_PR11;
}
}
You can still check somewhere if GPIOC->IDR actually reflects the button state, to eliminate possible hardware problems.
You can also try setting EXTI->SWIER from the debugger, or in the code, to simulate a button press.

Resources