Map interrupt handlers for SAM L10 device - arm

I have been attempting to set up the I2C on a system which uses the SAM L10 MCU. I have been able to build my project on MPLAB X as a standalone project without having to use MPLAB Harmony v3 code configurator. My problem is I can't seem to get the interrupt handlers to be mapped correctly.
Here are the references I am using:
https://github.com/Microchip-MPLAB-Harmony/csp_apps_sam_l10_l11/tree/master/apps/sercom/i2c/master/i2c_eeprom
and
https://microchipdeveloper.com/32arm:samd21-code-gcc-nvic-example
In both cases, there seems to be a table which assigns function pointers for the interrupt handlers, called exception_table. I do not know the correct way to incorporate this in my code, in fact, I don't understand how this code actually gets executed as it is simply assigning the function pointers.
Here is what I have for my code:
init.c
#include <xc.h>
#include "peripheral/clock/plib_clock.h"
#include "peripheral/nvic/plib_nvic.h"
#include "peripheral/sercom/plib_sercom0_i2c_master.h"
#pragma config NVMCTRL_NSULCK = 0x7
#pragma config BOD33_LEVEL = 0x6
#pragma config BOD33_DIS = CLEAR
#pragma config BOD33_ACTION = 0x1
#pragma config WDT_RUNSTDBY = CLEAR
#pragma config WDT_ENABLE = SET
#pragma config WDT_ALWAYSON = CLEAR
#pragma config WDT_PER = 0x9
#pragma config WDT_WINDOW = 0xB
#pragma config WDT_EWOFFSET = 0xB
#pragma config WDT_WEN = CLEAR
#pragma config BOD33_HYST = CLEAR
void port_init (void)
{
/* Configure SERCOM0 I2C on PA22 and PA23, Peripheral function C */
PORT_REGS->GROUP[0].PORT_PINCFG[22] = 0x1;
PORT_REGS->GROUP[0].PORT_PINCFG[23] = 0x1;
PORT_REGS->GROUP[0].PORT_PMUX[11] = 0x22;
/* Configure PA25 as an output driven high */
PORT_REGS->GROUP[0].PORT_DIR = 0x02000000;
PORT_REGS->GROUP[0].PORT_OUT = 0x02000000;
PORT_REGS->GROUP[0].PORT_PINCFG[27] = 0x0;
}
void sys_initialise (void)
{
CLOCK_Initialize ();
port_init ();
SERCOM0_I2C_Initialize ();
NVIC_Initialize ();
}
main.c
#include <xc.h>
#include "init.h"
#include "peripheral/sercom/plib_sercom0_i2c_master.h"
void main (void)
{
sys_initialise ();
uint8_t reg[1];
reg[0] = 0xD0;
SERCOM0_I2C_Write (0x76, &reg[0], 2);
}
I used the GPIO for my debugging. I thought setting up the NVIC would be enough but it only enables the interrupt themselves, the interrupt handler is still not mapped. I can see it because from my debugging the handler never gets called when I try to send something over I2C. I can also see the I2C work correctly for sending the first part of the transaction, which is the device address up to the ACK bit. From this point on, I can see on the scope that the clock is held low but the SDA comes back to high. I believe it is because the interrupt handler SERCOM0_I2C_InterruptHandler from https://github.com/Microchip-MPLAB-Harmony/csp_apps_sam_l10_l11/blob/master/apps/sercom/i2c/master/i2c_eeprom/firmware/src/config/sam_l10_xpro/peripheral/sercom/i2c_master/plib_sercom0_i2c_master.c does not get called, as this is responsible for sending the rest of the data.

I found the answer to this in the user guide for the xc32 compiler which can be accessed here: https://ww1.microchip.com/downloads/en/DeviceDoc/XC32_PICC_UG_DS50002895A.pdf
I was missing the call to NVIC_SetVector() - section 14.3 shows how to associate the interrupt handler with an exception.

Related

Source and Header file syntax in MPLAB X IDE v5.50

I am developing the code for my PIC32MK1024MCM project. I have already tested the code well and now I am only putting all the code modules into the final project (the code is not complete in this example yet, but the functionality is not the axis of interest here). For the first time in my life, I wanted to make it a little bit more professional and use separate source and header files for all the different module function declaration. However, I am clearly facing some kind of syntax problem, because I am getting errors in almost every line of the source file (I guess I have to include something in that source file, but I am not sure) Like I said, it is my very first time facing header and source files, so could you please help me, or at least hint me, what is it that I am missing so obviously? I want to thank you in advance.
main:
#include <xc.h>
#include <configuration_bits.c>
#include <toolchain_specifics.h>
#include <stddef.h>
#include <stdint.h>
#include <stdbool.h>
#include <stdlib.h>
#include "stdio.h"
#include <sys/attribs.h>
#include <analog_to_digital_conversion.h>
void main(void) {
while (1){
}
return (EXIT_FAILURE);
}
configurations_bits
// DEVCFG3
#pragma config USERID = 0xFFFF // Enter Hexadecimal value (Enter Hexadecimal value)
#pragma config PWMLOCK = OFF // PWM IOxCON lock (PWM IOxCON register writes accesses are not locked or protected)
#pragma config FUSBIDIO2 = OFF // USB2 USBID Selection (USBID pin is controlled by the port function)
#pragma config FVBUSIO2 = OFF // USB2 VBUSON Selection bit (VBUSON pin is controlled by the port function)
#pragma config PGL1WAY = OFF // Permission Group Lock One Way Configuration bit (Allow multiple reconfigurations)
#pragma config PMDL1WAY = OFF // Peripheral Module Disable Configuration (Allow multiple reconfigurations)
#pragma config IOL1WAY = OFF // Peripheral Pin Select Configuration (Allow multiple reconfigurations)
#pragma config FUSBIDIO1 = OFF // USB1 USBID Selection (USBID pin is controlled by the port function)
#pragma config FVBUSIO1 = OFF // USB2 VBUSON Selection bit (VBUSON pin is controlled by the port function)
// DEVCFG2
#pragma config FPLLIDIV = DIV_1 // System PLL Input Divider (1x Divider)
#pragma config FPLLRNG = RANGE_BYPASS // System PLL Input Range (Bypass)
#pragma config FPLLICLK = PLL_POSC // System PLL Input Clock Selection (POSC is input to the System PLL)
#pragma config FPLLMULT = MUL_4 // System PLL Multiplier (PLL Multiply by 4)
#pragma config FPLLODIV = DIV_2 // System PLL Output Clock Divider (2x Divider)
#pragma config BORSEL = HIGH // Brown-out trip voltage (BOR trip voltage 2.1v (Non-OPAMP deviced operation))
#pragma config UPLLEN = OFF // USB PLL Enable (USB PLL Disabled)
// DEVCFG1
#pragma config FNOSC = POSC // Oscillator Selection Bits (Primary Osc (HS,EC))
#pragma config DMTINTV = WIN_0 // DMT Count Window Interval (Window/Interval value is zero)
#pragma config FSOSCEN = OFF // Secondary Oscillator Enable (Disable Secondary Oscillator)
#pragma config IESO = ON // Internal/External Switch Over (Enabled)
#pragma config POSCMOD = HS // Primary Oscillator Configuration (HS osc mode)
#pragma config OSCIOFNC = OFF // CLKO Output Signal Active on the OSCO Pin (Disabled)
#pragma config FCKSM = CSDCMD // Clock Switching and Monitor Selection (Clock Switch Disabled, FSCM Disabled)
#pragma config WDTPS = PS1 // Watchdog Timer Postscaler (1:1)
#pragma config WDTSPGM = STOP // Watchdog Timer Stop During Flash Programming (WDT stops during Flash programming)
#pragma config WINDIS = NORMAL // Watchdog Timer Window Mode (Watchdog Timer is in non-Window mode)
#pragma config FWDTEN = OFF // Watchdog Timer Enable (WDT Disabled)
#pragma config FWDTWINSZ = WINSZ_25 // Watchdog Timer Window Size (Window size is 25%)
#pragma config DMTCNT = DMT31 // Deadman Timer Count Selection (2^31 (2147483648))
#pragma config FDMTEN = OFF // Deadman Timer Enable (Deadman Timer is disabled)
// DEVCFG0
#pragma config DEBUG = OFF // Background Debugger Enable (Debugger is disabled)
#pragma config JTAGEN = OFF // JTAG Enable (JTAG Disabled)
#pragma config ICESEL = ICS_PGx1 // ICE/ICD Comm Channel Select (Communicate on PGEC1/PGED1)
#pragma config TRCEN = OFF // Trace Enable (Trace features in the CPU are disabled)
#pragma config BOOTISA = MIPS32 // Boot ISA Selection (Boot code and Exception code is MIPS32)
#pragma config FECCCON = ECC_DECC_DISABLE_ECCON_WRITABLE // Dynamic Flash ECC Configuration Bits (ECC and Dynamic ECC are disabled (ECCCON<1:0> bits are writable))
#pragma config FSLEEP = OFF // Flash Sleep Mode (Flash is powered down when the device is in Sleep mode)
#pragma config DBGPER = PG_ALL // Debug Mode CPU Access Permission (Allow CPU access to all permission regions)
#pragma config SMCLR = MCLR_NORM // Soft Master Clear Enable (MCLR pin generates a normal system Reset)
#pragma config SOSCGAIN = G3 // Secondary Oscillator Gain Control bits (Gain is G3)
#pragma config SOSCBOOST = ON // Secondary Oscillator Boost Kick Start Enable bit (Boost the kick start of the oscillator)
#pragma config POSCGAIN = G3 // Primary Oscillator Coarse Gain Control bits (Gain Level 3 (highest))
#pragma config POSCBOOST = ON // Primary Oscillator Boost Kick Start Enable bit (Boost the kick start of the oscillator)
#pragma config POSCFGAIN = G3 // Primary Oscillator Fine Gain Control bits (Gain is G3)
#pragma config POSCAGCDLY = AGCRNG_x_25ms // AGC Gain Search Step Settling Time Control (Settling time = 25ms x AGCRNG)
#pragma config POSCAGCRNG = ONE_X // AGC Lock Range bit (Range 1x)
#pragma config POSCAGC = Automatic // Primary Oscillator Gain Control bit (Automatic Gain Control for Oscillator)
#pragma config EJTAGBEN = NORMAL // EJTAG Boot Enable (Normal EJTAG functionality)
// DEVCP
#pragma config CP = OFF // Code Protect (Protection Disabled)
// SEQ
#pragma config TSEQ = 0xFFFF // Boot Flash True Sequence Number (Enter Hexadecimal value)
#pragma config CSEQ = 0xFFFF // Boot Flash Complement Sequence Number (Enter Hexadecimal value)
analog_to_digital_conversion.h
//**************************************************************************
// ANALOG TO DIGITAL CONVERSION HEADER FILE
//**************************************************************************
#include <analog_to_digital_conversion.c>
void Anaolog_to_Digital_Conversion_Setup (void);
void Anaolog_to_Digital_Conversion_Enable (void);
void Anaolog_to_Digital_Conversion_Disable (void);
uint16_t Anaolog_to_Digital_Conversion (void);
analog_to_digital_conversion.c
//**************************************************************************
// ANALOG TO DIGITAL CONVERSION SOURCE FILE
//**************************************************************************
void Anaolog_to_Digital_Conversion_Setup (void){
//All this procedure is taken from the device`s datasheet (no ADC interrupts are desired)
ADCANCONbits.ANEN5 = 0b0; //Analog and bias circuitry disabled (to set calibration)
//---------------------------------------------------------------------------------------------------------------------------------------------------------------
ADC5CFGbits.ADCCFG = DEVADC5; //Copying the factory calibration ADC module bits to the ADC configuration register
//---------------------------------------------------------------------------------------------------------------------------------------------------------------
ADCCON1bits.ON = 0b0; //Disabling the ADC module
//---------------------------------------------------------------------------------------------------------------------------------------------------------------
ADC5TIMEbits.SAMC = 0b1111111111; //Sample time is set to 1025 TAD
ADC5TIMEbits.ADCDIV = 0b1111111; //254 * TQ = TAD (ADC clock division bits)
//---------------------------------------------------------------------------------------------------------------------------------------------------------------
ADCANCONbits.WKUPCLKCNT = 0xF; //ADC warm up time is set to 32768 ADC clock cycles (maximum warm up time, around 32 us # 100 MHz SYSCLK)
//---------------------------------------------------------------------------------------------------------------------------------------------------------------
ADCCON3bits.ADCSEL = 0b0; //Analog-to-Digital Clock Source (TCLK) -> SYSCLK
ADCCON3bits.CONCLKDIV = 0b000000; //TCLK = TQ
ADCCON3bits.DIGEN5 = 0b0; //All digital bits are disabled (according to the datasheet)
ADCCON3bits.VREFSEL = 0b000; //Vref is set to AVdd and AVss
//---------------------------------------------------------------------------------------------------------------------------------------------------------------
ADCIMCON1bits.DIFF11 = 0b0; //AN11 is using Single-ended mode
ADCIMCON1bits.SIGN11 = 0b0; //AN11 is using Unsigned Data mode
//---------------------------------------------------------------------------------------------------------------------------------------------------------------
ADCTRGSNSbits.LVL11 = 0b0; //Analog input is sensitive to the positive edge of its trigger (this is the value after a reset)
//---------------------------------------------------------------
ADCTRG3bits.TRGSRC11 = 0b00001; //AN11 is software triggered
//---------------------------------------------------------------------------------------------------------------------------------------------------------------
ADCANCONbits.ANEN5 = 0b1; //Analog and bias circuitry enabled
//---------------------------------------------------------------------------------------------------------------------------------------------------------------
ADCCON1bits.ON = 0b1; //Enabling the ADC module
//---------------------------------------------------------------
while(!((ADCCON2bits.BGVRRDY)&&(ADCANCONbits.WKRDY5))); //Wait until device analog environment is ready
ADCCON3bits.DIGEN5 = 0b1; //Enable digital circuitry for data processing
//---------------------------------------------------------------------------------------------------------------------------------------------------------------
ADCCON3bits.ADINSEL = 0b001011; //Select analog channel 11 for conversion
//---------------------------------------------------------------------------------------------------------------------------------------------------------------
ADCCON1bits.ON = 0b0; //Disabling the ADC module
}
void Anaolog_to_Digital_Conversion_Enable (void){
ADCCON1bits.ON = 0b1; //Enabling the ADC module
}
void Anaolog_to_Digital_Conversion_Disable (void){
ADCCON1bits.ON = 0b0; //Disabling the ADC module
}
uint16_t Anaolog_to_Digital_Conversion (void){
uint16_t ADC_value = 0;
ADCCON3bits.RQCNVRT = 1; //Trigger the conversion
while(!ADCDSTAT1bits.ARDY11); //Waiting until ADC result is ready to be read (# 100 MHz SYSCLK ADC conversion should take around 2ms)
ADC_value = ADCDATA11 & 0x000FFFF; //Acquiring ADC result, register is 32 bits, but conversion only gives 12 bit ADC value, hence getting rid of higher 16 bits (those are all zeros)
return (ADC_value);
}
As per the files in your question the inclusion should be done using header files since this is the common practice.
First of all you need to include the xc.h header in each file you use the processor specific definitions like special fuunction register and bit names.
Other dependencies for your program, might be anything from the standard C library like printf from the stdio.h header or uint8_t from the stdint.h header; or any library functions that you wrote for the application and so on.
Here is a trick for you: If you use MPLAB editor to write code, after typing a few letters of a function for example, you hit the Ctrl + Space then MPLAB editor will popup possible functions. When you select one of them from the popup list end hit the enter, it will add its header file automatically.
Now let's get back to your case. Your main file looks ok except stdio.h inclusion. You must change #include "stdio.h" to #include <stdio.h>.
Your adc.c file must have some includes as I mentioned above. First of all it must include the xc.h header. And you don't have to include the *.c file in the *.h file. I also recommend you to use header guards to prevent any possible nested inclusion, resulting in redefiniton errors.
analog_to_digital_conversion.h
// Header guard
#ifndef ANALOG_TO_DIGITAL_CONVERSION_H
#define ANALOG_TO_DIGITAL_CONVERSION_H
//**************************************************************************
// ANALOG TO DIGITAL CONVERSION HEADER FILE
//**************************************************************************
// #include <analog_to_digital_conversion.c> not needed here.
void Anaolog_to_Digital_Conversion_Setup (void);
void Anaolog_to_Digital_Conversion_Enable (void);
void Anaolog_to_Digital_Conversion_Disable (void);
uint16_t Anaolog_to_Digital_Conversion (void);
#endif /* ANALOG_TO_DIGITAL_CONVERSION_H */
analog_to_digital_conversion.c
//**************************************************************************
// ANALOG TO DIGITAL CONVERSION SOURCE FILE
//**************************************************************************
// include xc header for processor register and bit names
#include <xc.h>
// since you use uint16_t, you must include stdint header
#include <stdint.h>
// Finally include your own header for any further definitions
#include "analog_to_digital_conversion.h"
void Anaolog_to_Digital_Conversion_Setup (void){
//All this procedure is taken from the device`s datasheet (no ADC interrupts are desired)
ADCANCONbits.ANEN5 = 0b0; //Analog and bias circuitry disabled (to set calibration)
//---------------------------------------------------------------------------------------------------------------------------------------------------------------
ADC5CFGbits.ADCCFG = DEVADC5; //Copying the factory calibration ADC module bits to the ADC configuration register
//---------------------------------------------------------------------------------------------------------------------------------------------------------------
ADCCON1bits.ON = 0b0; //Disabling the ADC module
//---------------------------------------------------------------------------------------------------------------------------------------------------------------
ADC5TIMEbits.SAMC = 0b1111111111; //Sample time is set to 1025 TAD
ADC5TIMEbits.ADCDIV = 0b1111111; //254 * TQ = TAD (ADC clock division bits)
//---------------------------------------------------------------------------------------------------------------------------------------------------------------
ADCANCONbits.WKUPCLKCNT = 0xF; //ADC warm up time is set to 32768 ADC clock cycles (maximum warm up time, around 32 us # 100 MHz SYSCLK)
//---------------------------------------------------------------------------------------------------------------------------------------------------------------
ADCCON3bits.ADCSEL = 0b0; //Analog-to-Digital Clock Source (TCLK) -> SYSCLK
ADCCON3bits.CONCLKDIV = 0b000000; //TCLK = TQ
ADCCON3bits.DIGEN5 = 0b0; //All digital bits are disabled (according to the datasheet)
ADCCON3bits.VREFSEL = 0b000; //Vref is set to AVdd and AVss
//---------------------------------------------------------------------------------------------------------------------------------------------------------------
ADCIMCON1bits.DIFF11 = 0b0; //AN11 is using Single-ended mode
ADCIMCON1bits.SIGN11 = 0b0; //AN11 is using Unsigned Data mode
//---------------------------------------------------------------------------------------------------------------------------------------------------------------
ADCTRGSNSbits.LVL11 = 0b0; //Analog input is sensitive to the positive edge of its trigger (this is the value after a reset)
//---------------------------------------------------------------
ADCTRG3bits.TRGSRC11 = 0b00001; //AN11 is software triggered
//---------------------------------------------------------------------------------------------------------------------------------------------------------------
ADCANCONbits.ANEN5 = 0b1; //Analog and bias circuitry enabled
//---------------------------------------------------------------------------------------------------------------------------------------------------------------
ADCCON1bits.ON = 0b1; //Enabling the ADC module
//---------------------------------------------------------------
while(!((ADCCON2bits.BGVRRDY)&&(ADCANCONbits.WKRDY5))); //Wait until device analog environment is ready
ADCCON3bits.DIGEN5 = 0b1; //Enable digital circuitry for data processing
//---------------------------------------------------------------------------------------------------------------------------------------------------------------
ADCCON3bits.ADINSEL = 0b001011; //Select analog channel 11 for conversion
//---------------------------------------------------------------------------------------------------------------------------------------------------------------
ADCCON1bits.ON = 0b0; //Disabling the ADC module
}
void Anaolog_to_Digital_Conversion_Enable (void){
ADCCON1bits.ON = 0b1; //Enabling the ADC module
}
void Anaolog_to_Digital_Conversion_Disable (void){
ADCCON1bits.ON = 0b0; //Disabling the ADC module
}
uint16_t Anaolog_to_Digital_Conversion (void){
uint16_t ADC_value = 0;
ADCCON3bits.RQCNVRT = 1; //Trigger the conversion
while(!ADCDSTAT1bits.ARDY11); //Waiting until ADC result is ready to be read (# 100 MHz SYSCLK ADC conversion should take around 2ms)
ADC_value = ADCDATA11 & 0x000FFFF; //Acquiring ADC result, register is 32 bits, but conversion only gives 12 bit ADC value, hence getting rid of higher 16 bits (those are all zeros)
return (ADC_value);
}
You must use the same inclusion logic for each *.c & *.h file pair that you add to your project.
Separate compilation requires visibility of external symbols. For example in analog_to_digital_conversion.c where ADCANCONbits is referenced, the compiler needs to see at least a declaration of ADCANCONbits so that it knows that the symbol exists and what its type is. The actual linkage and resolution to a definition occurs when the linker pulse the object code from each separately compiled translation unit together.
You resolve undeclared identifier errors by providing the declaration. In this case that declaration exists inside a header file that you must #include in every translation unit that references it. I am not familiar with PIC32 development, but would imagine that header is xc.h
Yoiu have a number of other issues, for example analog_to_digital_conversion.h referenced uint16_t but does not include <stdint.h> where that type is defined. You may get away with it due to inclusion in other modules, but you should not rely on it.
Also all header files require guards to avoid multiple declarations when included more than once in the same translation unit - which can easily happen when you have nested includes for example.

How to properly set up DMA on a dsPIC33F

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)

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.

Unable to get the timer working

I have written simple timer program for Atmega328 in normal mode. But I am unable to flash the LED if I compile the code in Atmel Studio 6.2. But same code works perfect if I compile in arduino IDE. I have given the code for Arduino as well as Atmel Studio below. There seems to be small issue somewhere. Is there any issue with F_CPU value?
// Code compiled using Atmel Studio:
#include <avr/io.h>
#include <avr/interrupt.h>
#define F_CPU 16000000
unsigned char x=0;
ISR(TIMER1_OVF_vect) {
x=!x;
}
void setup() {
DDRB=0x01;
TIMSK1=0x01; // enabled global and timer overflow interrupt;
TCCR1A = 0x00; // normal operation page 148 (mode0);
TCNT1=0x0000; // 16bit counter register
TCCR1B = 0x04; // start timer/ set clock
};
int main (void)
{
setup();
while(1)
{
PORTB= x;
}
return(0);
}
Code written with Arduino IDE:
#define LED 8
boolean x=false;
ISR(TIMER1_OVF_vect) {
x=!x;
}
void setup() {
pinMode(LED, OUTPUT);
TIMSK1=0x01; // enabled global and timer overflow interrupt;
TCCR1A = 0x00; // normal operation page 148 (mode0);
TCNT1=0x0000; // 16bit counter register
TCCR1B = 0x04; // start timer/ set clock
};
void loop() {
PORTB= x;
}
When working with interrupts, you need to enable both the global interrupts (in the timer register) and the local interrupts (in the status registers) in order for interrupt vectors to trigger.
This can be done by calling sei() (set enable interrupts) when you are ready to receive local interrupts. Typically you want to do this after you set up the global interrupts, near the end of you setup method.
I suspect that the interrupts are automatically enabled when working with the Arduino IDE.

simple pic10f204 C programing

I'm new to microcontrollers and picked out a PIC10f204. I am using MPLAB XIDE with the free XC8 C compiler.
I'm trying to slowly learn right now. All i am trying to do is set GPIO bit 0, which is the GP0 pin out, to out put high.
So far my code looks like this but I am not measuring 5V from the pin GP0 or any of the other GPIO pins, except GP3.
#include <xc.h>
#include <stdio.h>
#include <stdlib.h>
// CONFIG
#pragma config WDTE = OFF // Watchdog Timer (WDT disabled)
#pragma config CP = OFF // Code Protect (Code protection off)
#pragma config MCLRE = OFF // Master Clear Enable (GP3/MCLR pin fuction is digital I/O, MCLR internally tied to VDD)
int main(void) {
OSCCAL= 0x00;
TRISGPIO = 0x00;
GPIO= 0xFF;
return 0; // we should never reach this
}
Any help would be much appreciated.
Thanks!
A couple changes might help:
1) Try adding an infinite loop in your code. The program on a microcontroller should follow a general structure of initialization functions followed by an infinite loop.
2) Looking at the datasheet for the PIC10F204, it says that pin 3 is input only. Try modifying as follows: TRISGPIO = 0x08;
I modified your code below to illustrate this. Hopefully this works for you.
int main(void) {
// PIC Initializations should go here
OSCCAL= 0x00;
CMCON0 = 0x51;
TRISGPIO = 0x08;
GPIO= 0xFF;
while(1) {
// Program main loop (should never end)
}
return 0; // we should never reach this
}

Resources