I'm trying to get a fundamental understanding of programming for STM32 microcontrollers. I'm trying to use an external interrupt from a button to toggle the state of an LED, by setting all the appropriate registers (no external libraries/definitions). I can't seem to get the interrupt routine to work, control is never passed to the handler.
Here's what I'm using to set PC-13 (to which there is a button attached as per the Nucleo-G070RB board) as an external interrupt:
RCC_APBENR2 |= 0b1 << 0; // Enable SYSCFGEN
RCC_IOPENR |= 0b1 << 2; // Enable PORTC
EXTI_RTSR1 |= 0b1 << 13; // Enable interrupt on rising edge at EXTI line 13
EXTI_EXTICR4 |= 0x2 << 8; // Set PC-13 as GPIO pin for interrupt
EXTI_IMR1 |= 0x1 << 13; // Unmask EXTI line 13
NVIC_ISER |= 0b1 << 7; // Enable interrupts for EXTI4_15
I'm not sure what I'm missing.
Here's the full code:
#include <stdint.h>
#include <stdio.h>
#define RCC_IOPENR *((uint32_t volatile *) 0x40021034)
#define RCC_APBENR2 *((uint32_t volatile *) 0x40021040)
#define GPIOA_MODER *((uint32_t volatile *) 0x50000000)
#define GPIOA_IDR *((uint32_t volatile *) 0x50000010)
#define GPIOA_ODR *((uint32_t volatile *) 0x50000014)
#define GPIOC_MODER *((uint32_t volatile *) 0x50000800)
#define GPIOC_IDR *((uint32_t volatile *) 0x50000810)
#define GPIOC_ODR *((uint32_t volatile *) 0x50000814)
#define NVIC_ISER *((uint32_t volatile *) 0xE000E100)
#define EXTI_RTSR1 *((uint32_t volatile *) 0x40021800)
#define EXTI_EXTICR1 *((uint32_t volatile *) (0x40021800 + 0x060 + 0x4 * 0))
#define EXTI_EXTICR2 *((uint32_t volatile *) (0x40021800 + 0x060 + 0x4 * 1))
#define EXTI_EXTICR3 *((uint32_t volatile *) (0x40021800 + 0x060 + 0x4 * 2))
#define EXTI_EXTICR4 *((uint32_t volatile *) (0x40021800 + 0x060 + 0x4 * 3))
#define EXTI_IMR1 *((uint32_t volatile *) (0x40021800 + 0x080))
#define EXTI_RPR1 *((uint32_t volatile *) (0x40021800 + 0x00C))
void button_init();
extern void initialise_monitor_handles();
uint8_t volatile g_button_pressed = 0;
int main(void) {
//asm volatile ("cpsie i");
initialise_monitor_handles();
//printf("Hello World!\n");
RCC_IOPENR |= 0b1 << 0; // Enable PORTA
GPIOA_MODER &= ~(0b11 << (2*5)); // Clear MODE bits for 5th pin (PA-5)
GPIOA_MODER |= 0b1 << (2*5); // Set MODE bits for 5th pin (PA-5) to 01 (set output)
GPIOC_MODER &= ~(0b11 << (2*13)); // Clear MODE bits for 13th pin (PC-13) (set input)
button_init();
while(1) {
if (g_button_pressed) {
GPIOA_ODR ^= 0b1 << 5;
printf("Button pressed\n");
g_button_pressed = 0;
}
}
}
void button_init() {
RCC_APBENR2 |= 0b1 << 0; // Enable SYSCFGEN
RCC_IOPENR |= 0b1 << 2; // Enable PORTC
EXTI_RTSR1 |= 0b1 << 13; // Enable interrupt on rising edge at EXTI line 13
EXTI_EXTICR4 |= 0x2 << 8; // Set PC-13 as GPIO pin for interrupt
EXTI_IMR1 |= 0x1 << 13; // Unmask EXTI line 13
NVIC_ISER |= 0b1 << 7; // Enable interrupts for EXTI4_15
}
void EXTI4_15_IRQHandler(void) {
g_button_pressed = 1;
printf("Button pressed\n");
EXTI_RPR1 |= 0xFFFF << 0;
}
I'm using the STM32CubeIDE with the default compiler, and and printing via semihosting with OpenOCD.
So, my question is, am I missing a step or doing something wrong?
So, I figured out what my dumb mistake was. I was so focused in trying to understand where I had gone wrong in setting up the external interrupt, I didn't really check if I had set up the button correctly.
I tried to set PC13 as an input:
GPIOC_MODER &= ~(0b11 << (2*13)); // Clear MODE bits for 13th pin (PC-13) (set input)
before I called my button_init() function, which enables the clock for GPIOC via:
RCC_IOPENR |= 0b1 << 2; // Enable PORTC
Since GPIOC was not enabled, PC13 was not set as an input either, so there was nothing to detect.
Here's the fixed code: (I also updated it to use the CMSIS stm32 headers, as suggested by P__J__)
#define STM32G070xx
#include "stm32g0xx.h"
uint8_t volatile g_button_pressed = 0;
void button_init();
//extern void initialise_monitor_handles();
int main(void) {
// initialise_monitor_handles();
//printf("Hello World!\n");
RCC->IOPENR |= 0b1 << 0; // Enable PORTA
GPIOA->MODER &= ~(0b11 << (2*5)); // Clear MODE bits for 5th pin (PA-5)
GPIOA->MODER |= 0b1 << (2*5); // Set MODE bits for 5th pin (PA-5) to 01 (set output)
button_init();
while(1) {
if (g_button_pressed) {
GPIOA->ODR ^= 0b1 << 5;
// printf("Button pressed! Yay!\n");
g_button_pressed = 0;
}
}
}
void button_init() {
// RCC->APBENR2 |= (uint32_t) (0b1 << 0); // Enable SYSCFGEN
RCC->IOPENR |= (uint32_t) (0b1 << 2); // Enable PORTC
GPIOC->MODER &= ~(0b11 << (2*13)); // Clear MODE bits for 13th pin (PC-13) (set input)
EXTI->RTSR1 |= (uint32_t) (0b1 << 13); // Enable interrupt on rising edge at EXTI line 13
EXTI->EXTICR[3] |= (uint32_t) (0x2 << 8); // Set PC-13 as GPIO pin for interrupt
EXTI->IMR1 |= (uint32_t) (0x1 << 13); // Unmask EXTI line 13
NVIC->ISER[0] |= (uint32_t) (0b1 << 7); // Enable interrupts for EXTI4_15
}
void EXTI4_15_IRQHandler(void) {
g_button_pressed = 1;
// printf("Button pressed\n");
EXTI->RPR1 = 0b1 << 13;
}
Related
In the code I wrote, I chose the PA1 port as an input and connected a button to this input port, then I wrote an interrupt to this button.
first, I write like this and it didn't work
EXTI->EXTICR[0] |= (0U << 8*1);
then, I wrote like this,
EXTI->EXTICR[0] |= (1U << 8*3); and it worked.
I am trying to make PA1 to be an input. and A = 0 this is why i want to write 0U and i want to write 8*1 cause of port is Px1.
my code :
/* External interrupt at PA1 port */
EXTI->EXTICR[0] |= (1U << 8*3);
But I think it should be like,
/* External interrupt at PA1 port */
EXTI->EXTICR[0] |= (0U << 8*1);
Can you explain, why PA1 input is (1U << 83) and not (0U << 81)
When I write
/* External interrupt at PA1 port */
EXTI->EXTICR[0] |= (1U << 8*3);
This, the program is working successfully. But I didn't understand why I have to write (1U << 83), shouldn't I write (0U << 81)?
This is my FULL code :
/*
* project name : 05_EXTIbuttonSM
* file name : main.c
*
* author : Cem Furkan Demirkıran
*
* date : 2022.10.27
*
* description : A state machine blink the external LED at different intervals.
* Assigned each speed to a mode, and attach a button to cycle through the modes.
* Used external interrupts to detect button presses and used the handler to
* change the states. Each button press will cycle through these modes.
*/
#include "stm32g0xx.h"
/* 1 Sec is 1600000 */
void delay(volatile uint32_t);
uint32_t ButtonPress = 0;
/* Interrupt Handlers */
void EXTI0_1_IRQHandler(void){
if (ButtonPress != 5) {
ButtonPress ++;
}
else {
ButtonPress = 0;
}
delay(100000);
EXTI->RPR1 |= (1U << 1);
}
int main(void) {
/* Enable GPIOA clock */
RCC->IOPENR |= (1U << 0);
/* Setup PA0 as output */
GPIOA->MODER &= ~(3U << 2*0);
GPIOA->MODER |= (1U << 2*0);
/* Setup PA1 as input */
GPIOA->MODER &= ~(3U << 2*1);
GPIOA->MODER |= (0U << 2*1);
/* External interrupt at PA1 port */
EXTI->EXTICR[0] |= (1U << 8*3);
/* Mask and Rising on Px1 */
EXTI->IMR1 |= (1U << 1);
EXTI->RTSR1 |= (1U << 1);
/* Setup NVIC */
NVIC_SetPriority(EXTI0_1_IRQn, 0);
NVIC_EnableIRQ(EXTI0_1_IRQn);
while(1) {
switch(ButtonPress){
case 0:
/* Turn off LED */
GPIOA->ODR = (0U << 0);
break;
case 1:
/* Turn on LED 2sec interval */
GPIOA->ODR |= (1U << 0);
delay(3200000);
GPIOA->ODR ^= (1U << 0);
delay(3200000);
break;
case 2:
/* Turn on LED 1sec interval */
GPIOA->ODR |= (1U << 0);
delay(1600000);
GPIOA->ODR ^= (1U << 0);
delay(1600000);
break;
case 3:
/* Turn on LED 0.5sec interval */
GPIOA->ODR |= (1U << 0);
delay(800000);
GPIOA->ODR ^= (1U << 0);
delay(800000);
break;
case 4:
/* Turn on LED 0.1sec interval */
GPIOA->ODR |= (1U << 0);
delay(160000);
GPIOA->ODR ^= (1U << 0);
delay(160000);
break;
case 5:
/* Turn on LED */
GPIOA->ODR |= (1U << 0);
break;
}
}
return 0;
}
void delay(volatile uint32_t s) {
for(; s>0; s--);
}
This is working code after the Answers.
Added Volatile
Changing (1U << 83) to (0U << 81)
Also Changing Handler for bouncing problem.
/*
* project name : 05_EXTIbuttonSM
* file name : main.c
*
* author : Cem Furkan Demirkıran
*
* date : 2022.10.27
*
* description : A state machine blink the external LED at different intervals.
* Assigned each speed to a mode, and attach a button to cycle through the modes.
* Used external interrupts to detect button presses and used the handler to
* change the states. Each button press will cycle through these modes.
*/
#include "stm32g0xx.h"
/* 1 Sec is 1600000 */
void delay(volatile uint32_t);
volatile uint32_t ButtonPress = 0;
/* Interrupt Handlers */
void EXTI0_1_IRQHandler(void){
if (ButtonPress != 5) {
delay(50);
if(ButtonPress !=5)
ButtonPress ++;
else
ButtonPress = 0;
}
else {
ButtonPress = 0;
}
delay(100000);
EXTI->RPR1 |= (1U << 1);
}
int main(void) {
/* Enable GPIOA clock */
RCC->IOPENR |= (1U << 0);
/* Setup PA0 as output */
GPIOA->MODER &= ~(3U << 2*0);
GPIOA->MODER |= (1U << 2*0);
/* Setup PA1 as input */
GPIOA->MODER &= ~(3U << 2*1);
GPIOA->MODER |= (0U << 2*1);
/* External interrupt at PA1 port */
EXTI->EXTICR[0] |= (0U << 8*1);
/* Mask and Rising on Px1 */
EXTI->IMR1 |= (1U << 1);
EXTI->RTSR1 |= (1U << 1);
/* Setup NVIC */
NVIC_SetPriority(EXTI0_1_IRQn, 0);
NVIC_EnableIRQ(EXTI0_1_IRQn);
while(1) {
switch(ButtonPress){
case 0:
/* Turn off LED */
GPIOA->ODR = (0U << 0);
break;
case 1:
/* Turn on LED 2sec interval */
GPIOA->ODR |= (1U << 0);
delay(3200000);
GPIOA->ODR ^= (1U << 0);
delay(3200000);
break;
case 2:
/* Turn on LED 1sec interval */
GPIOA->ODR |= (1U << 0);
delay(1600000);
GPIOA->ODR ^= (1U << 0);
delay(1600000);
break;
case 3:
/* Turn on LED 0.5sec interval */
GPIOA->ODR |= (1U << 0);
delay(800000);
GPIOA->ODR ^= (1U << 0);
delay(800000);
break;
case 4:
/* Turn on LED 0.1sec interval */
GPIOA->ODR |= (1U << 0);
delay(160000);
GPIOA->ODR ^= (1U << 0);
delay(160000);
break;
case 5:
/* Turn on LED */
GPIOA->ODR |= (1U << 0);
break;
}
}
return 0;
}
void delay(volatile uint32_t s) {
for(; s>0; s--);
}
In this case, 8 means the number of bits per bitfield, and 3 or 1 is the number of whole bitfields you want to skip. The 0 or 1 on the left is the value you put in the bitfield.
I would advise you to use named constants here rather than magic numbers, to make the code easier to read.
If you do:
EXTI->EXTICR[0] |= (1U << 8*3);
This means skip 3 fields each of 8 bits and put the value 1 in the fourth field. This sets up an interrupt on PB3.
EXTI->EXTICR[0] |= (0U << 8*1);
This means skip 1 field of 8 bits and put the value 0 in the second field. This sets up an interrupt on PA1.
Note also that using |= you are only setting bits. If the peripheral is in its default reset state of having the register at all zeros then this is fine, but if you have already written to the field you want to change then you may also need to clear some bits with & and '~' too.
I'm testing some things on a Attiny85 and thought about the best way to handle the interrupt rutine. I know it is bad to have a lot of code in the interrupt handler, but I'm uncertain of any other ways to do this. I want my main program to sleep and wake on PCINT, the PCINT comes from multiple pins (rotary encoder A, b & switch and a receiving UART) so I was thinking just having a lot of code in the handler.
The code to determining which pin caused the interrupt, would look like this
#include <avr/io.h>
#include <stdint.h> // has to be added to use uint8_t
#include <avr/interrupt.h> // Needed to use interrupts
volatile uint8_t portbhistory = 0xFF; // default is high because the pull-up
int main(void)
{
DDRB &= ~((1 << DDB0) | (1 << DDB1) | (1 << DDB2)); // Clear the PB0, PB1, PB2 pin
// PB0,PB1,PB2 (PCINT0, PCINT1, PCINT2 pin) are now inputs
PORTB |= ((1 << PORTB0) | (1 << PORTB1) | (1 << PORTB2)); // turn On the Pull-up
// PB0, PB1 and PB2 are now inputs with pull-up enabled
PCICR |= (1 << PCIE0); // set PCIE0 to enable PCMSK0 scan
PCMSK0 |= (1 << PCINT0); // set PCINT0 to trigger an interrupt on state change
sei(); // turn on interrupts
while(1)
{
/*main program loop here */
}
}
ISR (PCINT0_vect)
{
uint8_t changedbits;
changedbits = PINB ^ portbhistory;
portbhistory = PINB;
if(changedbits & (1 << PB0))
{
/* PCINT0 changed */
}
if(changedbits & (1 << PB1))
{
/* PCINT1 changed */
}
if(changedbits & (1 << PB2))
{
/* PCINT2 changed */
}
}
And then ofc inside each of the if-statements in the interrupt handler, there would be code handling something, like this code, turning on the Timer0
TCNT0 = 0; // Set counter to 0
OCR0A = SERIAL_BIT_TIME; // Call timer interrupt in middle of first bit
position = 0; // Reset position and data
TIMSK |= 1 << OCIE0A; // Enable interrupt for compare register A (timer interrupt)
TIFR |= 1 << OCF0A; // Clear timer interrupt flag to prevent it jumping directly there
PCMSK &= ~(1 << SERIAL_RECEIVE); // Disable pin change interrupt
or with the switch input, the code inside the if-statement would be
if (lightState)
{
dali.transmit(ADDRESS, OFF);
lightState = 0;
}
else
{
dali.transmit(ADDRESS, ON);
lightState = 1;
}
Would this be a dumb solution?
volatile uint8_t flag;
int main(void)
{
DDRB &= ~((1 << DDB0) | (1 << DDB1) | (1 << DDB2)); // Clear the PB0, PB1, PB2 pin
// PB0,PB1,PB2 (PCINT0, PCINT1, PCINT2 pin) are now inputs
PORTB |= ((1 << PORTB0) | (1 << PORTB1) | (1 << PORTB2)); // turn On the Pull-up
// PB0, PB1 and PB2 are now inputs with pull-up enabled
PCICR |= (1 << PCIE0); // set PCIE0 to enable PCMSK0 scan
PCMSK0 |= (1 << PCINT0); // set PCINT0 to trigger an interrupt on state change
sei(); // turn on interrupts
while(1)
{
gotosleep();
do
{
switch(flag)
{
case 1:
dosomething1();
break;
case 2:
dosomething2();
break;
case 3:
dosomething3();
break;
}
cli();
flag = 0;
sei();
}while(flag);
}
}
ISR (PCINT0_vect)
{
uint8_t changedbits;
changedbits = PINB ^ portbhistory;
portbhistory = PINB;
if(changedbits & (1 << PB0))
{
flag = 1;
}
if(changedbits & (1 << PB1))
{
flag = 2;
}
if(changedbits & (1 << PB2))
{
flag = 3;
}
}
The code compiles correctly but I am unable to obtain 1kHz Square Wave. I attached an LED at PINB1 to check.
I used Timer1, with CTC mode and Prescalar as 64.
PLease Help.
#define F_CPU 8000000L
#include <avr/io.h>
#include "avr/iom32.h"
// - - - - PROGRAM TO GENERATE A SQUARE WAVE OF 1KHz - - - - //
void _delay_();
int main(void)
{
DDRB = 0xFF;
OCR1AH = 0xF4;
OCR1AL = 0x23;
TCNT1H = 0;
TCNT1L = 0;
while (1)
{
PORTB |= (1 << 4);
_delay_();
PORTB &= ~(1 << 4);
_delay_();
}
}
void _delay_() {
TCCR1A = 0x00;
TCCR1B = 0x0B;
while(!(TIFR & (1 << 4)));
TCCR1B = 0x00;
TCCR1A = 0x00;
TIFR |= 0x10;
}
As oldtimer suggested, you should use a o-scope to verify the output. If you don't have one, or if you do and still no output, then try replacing the delay routine with a simple software delay such as this:
void _delay_() {
// simple software delay
for (uint32_t i = 0; i < 50000; i++);
}
The idea is to adjust the maximum count (50000) to any value that creates a long enough delay to see the LED blink. If the LED still doesn't blink, then the problem is with the other code, or the external connection to the LED. For example, you say PINB1, but isn't that at bit position 1 << 1, but your code uses 1 << 4.
I want to learn more about the shiftout function used with Arduinos and used the implementation with my ATMEGA328p.
The Setup
#define F_CPU 1000000UL
#define BAUD 9600UL
#define LSBFIRST 0
#define MSBFIRST 1
#define HIGH 0x1
#define LOW 0x0
#define INPUT 0x0
#define OUTPUT 0x1
#include <avr/io.h>
#include <util/delay.h>
#include <USART.h>
#include <pinDefines.h>
#define DATA PD7
#define LATCH PB2
#define CLOCK PB0
uint8_t leds = 0;
void shiftOut(uint8_t dataPin, uint8_t clockPin, uint8_t bitOrder, uint8_t val)
{
for (uint8_t i = 0; i < 8; ++i) {
if (bitOrder == LSBFIRST) {
PORTD |= (!!(val & (HIGH << i)) << DATA);
} else {
PORTD |= (!!(val & (HIGH << (7 - i))) << DATA);
}
PORTB |= (HIGH << clockPin);
PORTB |= (LOW << clockPin);
}
}
void updateShiftRegister() {
PORTB |= (LOW << LATCH);
shiftOut(DATA, CLOCK, LSBFIRST, leds);
PORTB |= (HIGH << LATCH);
}
void setupRegisters() {
DDRD = 0xff;
DDRB = 0xff;
}
int main(void)
{
setupRegisters();
leds = 1;
updateShiftRegister();
_delay_ms(2000);
leds = 0;
updateShiftRegister();
return (0);
}
I created an example to understand more about the function, but the outcome does not add up to what I expected to see.
Given my example, I would expect LED1 to turn on and turn off after 2sec. It turns on, but it does not turn off afterwards. Did I miss anything? I am searching for 2hrs now for the error in my code.
DDRD is the "Data Direction Register D". If you want to use the whole Port D as output, you should set it to 0xFF, else parts of it are used as input, what causes your unexepected results.
I haven't checked, but it's possible that Port D is used by something more than just the General Digital I/O (e.g. by JTAG) what overrides the PORTD settings. Just read the datasheet. By the way, there also are some example programs.
I am learning embedded development on the STM3220G-EVAL board with the STM32F207 microcontroller. I have tried to test the I2C interface by interfacing the two I2C2 and I2C3 modules on the same chip and sending/receiving a character. Here is the code I have currently written (using mdk-arm 5):
#include <stm32f2xx.h>
volatile uint8_t data = 'a', recv = 'x';
void i2c_init(void);
void I2C2_EV_IRQHandler(void)
{
volatile uint16_t stat, dummy;
stat = I2C2->SR1;
switch(stat)
{
// SB set; read SR1 and write slave address in DR to clear
case 0x01:
dummy = I2C2->SR1;
// Send address of slave
I2C2->DR = (0x08 << 1);
break;
// ADDR set; read SR1 and SR2 to clear
case 0x02:
dummy = I2C2->SR1;
dummy = I2C2->SR2;
break;
// TxE set; write to DR to clear
case 0x80:
I2C2->DR = data;
break;
// TxE and BTF set; generate stop condition to clear
case 0x84:
// Generate stop
I2C2->CR1 |= (1 << 9);
break;
}
}
void I2C3_EV_IRQHandler(void)
{
volatile uint16_t stat, dummy;
stat = I2C3->SR1;
switch(stat)
{
// ADDR set; read SR1 and SR2 to clear
case 0x02:
dummy = I2C3->SR1;
dummy = I2C3->SR2;
break;
// STOPF set; read SR1 and write CR1 to clear
case 0x10:
dummy = I2C3->SR1;
I2C3->CR1 &= ~(1 << 0);
break;
// RxNE set; read DR to clear
case 0x40:
recv = I2C3->DR;
break;
}
}
int main()
{
i2c_init();
// Generate START condition
I2C2->CR1 |= (1 << 8);
while(1)
{
if(!(I2C2->OAR1 & (1 << 14)))
I2C2->OAR1 |= (1 << 14);
if(!(I2C3->OAR1 & (1 << 14)))
I2C3->OAR1 |= (1 << 14);
if(recv != 'x')
break;
}
return 0;
}
void i2c_init(void)
{
// Enable GPIOA, GPIOC, GPIOF, I2C2 and I2C3 peripherals
RCC->AHB1ENR |= (1 << 0);
RCC->AHB1ENR |= (1 << 2);
RCC->AHB1ENR |= (1 << 5);
RCC->APB1ENR |= (1 << 22);
RCC->APB1ENR |= (1 << 23);
// Set GPIO mode to AF
GPIOA->MODER |= (1 << 17);
GPIOC->MODER |= (1 << 19);
GPIOF->MODER |= (1 << 1);
GPIOF->MODER |= (1 << 3);
// Set GPIO type to OD
GPIOA->OTYPER |= (1 << 8);
GPIOC->OTYPER |= (1 << 9);
GPIOF->OTYPER |= (1 << 0);
GPIOF->OTYPER |= (1 << 1);
// Set GPIO speed to 50MHz
GPIOA->OSPEEDR |= (1 << 17);
GPIOC->OSPEEDR |= (1 << 19);
GPIOF->OSPEEDR |= (1 << 1);
GPIOF->OSPEEDR |= (1 << 3);
// Link to AFs
GPIOA->AFR[1] |= (1 << 2);
GPIOC->AFR[1] |= (1 << 6);
GPIOF->AFR[0] |= (1 << 2);
GPIOF->AFR[0] |= (1 << 6);
// Reset clocks
I2C2->CR2 = 0x00;
I2C3->CR2 = 0x00;
I2C2->CCR = 0x00;
I2C3->CCR = 0x00;
// Enable interrupts
I2C2->CR2 |= (1 << 9);
I2C2->CR2 |= (1 << 10);
I2C3->CR2 |= (1 << 9);
I2C3->CR2 |= (1 << 10);
NVIC_EnableIRQ(I2C2_EV_IRQn);
NVIC_EnableIRQ(I2C3_EV_IRQn);
// Must set bit 14 in OAR1 to 1
I2C2->OAR1 |= (1 << 14);
I2C3->OAR1 |= (1 << 14);
// Set addresses
I2C2->OAR1 = (0x04 << 1);
I2C3->OAR1 = (0x08 << 1);
// Set peripheral clock frequency
I2C2->CR2 |= 0x08;
I2C3->CR2 |= 0x08;
I2C2->CCR |= 0x28;
I2C3->CCR |= 0x28;
I2C2->TRISE = 0x09;
I2C3->TRISE = 0x09;
// Enable ACK
I2C2->CR1 |= (1 << 10);
I2C3->CR1 |= (1 << 10);
// Enable I2C peripherals
I2C2->CR1 |= (1 << 0);
I2C3->CR1 |= (1 << 0);
}
The problems I am facing are:
The execution never goes into the interrupt handlers (verified by
breakpoints)
The SB bit in SR1 of the master (I2C2) is never set even though i have set the START bit in CR1
The SDA line is HIGH but the SCL line is pulled LOW
I am using a pullup of 13K on SDA and 10K on SCL. Pin numbers used are PF0, PF1 (I2C2 SDA, SCL) and PA8, PC9 (I2C3 SCL, SDA). Using the internal or external pullups causes the SR2 register to display that the bus is busy.
Also I have not enabled I2C2 and I2C3 in RTE_Device.h. It just seems to provide convenience typedefs. (EDIT : Tried to enable these, it does not help)
Could anyone help me in solving this problem? I seem to have hit a dead end.
(EDIT : After setting up a few jumpers on the board, the master event handler is successfully being called. But still some problems persist. Now the acknowledge failure bit is being set, and the slave handler is not called. Bus lines have been verified to be HIGH when idle.)
Sorry for the delay in mentioning this, but I have successfully solved this problem by using the STM32 CPAL library available from ST. I have tested this library with the onboard accelerometer by reading the 'WHO_AM_I' register in the accelerometer. The code for this is:
#include "cpal_i2c.h"
int main()
{
// Configuration
CPAL_TransferTypeDef RxStruct;
uint8_t RxBuf;
RxStruct.pbBuffer = &RxBuf;
RxStruct.wAddr1 = 0x39;
// Initialization
CPAL_I2C_StructInit(&I2C1_DevStructure);
I2C1_DevStructure.CPAL_Mode = CPAL_MODE_MASTER;
I2C1_DevStructure.CPAL_ProgModel = CPAL_PROGMODEL_DMA;
I2C1_DevStructure.pCPAL_I2C_Struct->I2C_ClockSpeed = 100000;
I2C1_DevStructure.pCPAL_TransferRx = &RxStruct;
I2C1_DevStructure.pCPAL_TransferTx = pNULL;
CPAL_I2C_Init(&I2C1_DevStructure);
// Communication
RxStruct.wNumData = 1;
RxStruct.wAddr2 = 0x0F;
if(CPAL_I2C_Read(&I2C1_DevStructure) != CPAL_PASS)
{
// Error
}
while(I2C1_DevStructure.CPAL_State != CPAL_STATE_READY);
while(1);
return 0;
}