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;
}
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;
}
}
I'm trying to get encoder data to my stm32 h7 and display counter value through uart.
I can see counter value 0 through my uart terminal but counter value never goes up...
I'm using encoder mode.
Please, help me to do this
static void Encoder_init(void)
{
RCC->APB2RSTR &= ~(1 << 1);
RCC->APB2ENR |= (1 << 1); //TIM8 clk enable
TIM8->CR1 &= ~(1 << 0); //tim8 disable
TIM8->SMCR |= (0x03 << 0); //SMS set
TIM8->CCER &= ~(1 << 1); //CC1P
TIM8->CCER &= ~(1 << 5); //CC2P
TIM8->CCER &= ~(1<<3); //CC1NP LOW
TIM8->CCER &= ~(1<<7); //CC2NP LOW
TIM8->CCMR1 |= (1<<0); //CC1S
TIM8->CCMR1 |= (1<<8); //CC2S
TIM8->PSC = 0; //prescaler zero
TIM8->ARR = 0xFFFF;
TIM8->CR1 |= (1 << 0); //tim8 enable}
}
void tEncoder(void *pvParameters)
{
uint8_t encoder_data;
char buf[4];
char val;
RCC->AHB4RSTR &= ~(1 << 2);
RCC->AHB4ENR |= (1 << 2);
GPIOC->MODER &= ~(0x03 << 12);
GPIOC->MODER &= ~(0x03 << 14);
GPIOC->PUPDR |= (1<<6) | (1<<7);
Encoder_init();
while(1) {
vTaskDelay(1000);
if(encoder_data!=TIM8->CNT) {
encoder_data = TIM8->CNT;
int2str(buf, encoder_data);
uart7_buffer_putstr(buf);
SystemPrint("tEncoder counting\n\r");
}
}
}
I want to transfer data from an external AD-Converter. I get parallel 11 bits which I store in an array. I wanna set up a timer triggered GPDMA transfer, that every 50ns fires a DMA stream and transfer data. After the transfer is complete, the RAM address should be incremented.
The MCU, I work with is the LPC4088 Quickstart Board.
The CPU and Peripheral Clock, runs with 120Mhz
I studied the UM of LPC4088 and already wrote some simple basic code.
#include "LPC407x_8x_177x_8x.h"
#define PERIPHSRAM1 0x20004000
char adcData[11] = {1,1,1,1,1,1,1,1,1,1,1};
volatile uint32_t DMATCCOUNT = 0;
volatile uint32_t DMAERRCOUNT = 0;
int main(){
uint8_t size = sizeof(adcData);
LPC_SC->PCONP |= (1 << 22); //Power & Clock Timer2
LPC_TIM2->CTCR = (0 << 0); //Timer Mode
LPC_TIM2->PR = 0;
LPC_TIM2->TCR = (1 << 1); //Reset timer
LPC_TIM2->TC = 0;
LPC_TIM2->MR0 = 5; //Match register to get 10MHz
LPC_TIM2->MCR = (1 << 1) //Reset Timer
|(1 << 0); //Interrupt if MR0 matches
LPC_TIM2->EMR = 0x31; //toggle pin to check wave output
LPC_TIM2->TCR = (1 << 1); //reset timer again
LPC_SC->PCONP |= (1 << 29); //Power & Clock DMA
LPC_GPDMACH0->CConfig = 0; //disable CH0
LPC_GPDMA->Config |= (1 << 0); //enable DMA Controller
LPC_SC->DMAREQSEL = 0x010; //Set T2 MAT0 as DMA request
LPC_GPDMA->IntErrClr |= 0x0FF;
LPC_GPDMA->IntTCClear |= 0x0FF;
LPC_GPDMACH0->CDestAddr = PERIPHSRAM1;
LPC_GPDMACH0->CSrcAddr = (uint32_t) &adcData;
LPC_GPDMACH0->CControl = size
|(0x00 << 12) //Source Burst size: 1
|(0x00 << 15) //Destination Burst size: 1
|(0x02 << 18) //Source width: 32 bit
|(0x02 << 21) //Destination width:32 bit
|(0x01 << 26) //Source increment: enabled
|(0x01 << 27) //Destination increment: enabled
|(0x01 << 31); //TermCount interrupt enabled
LPC_GPDMACH0->CControl |= (4 << 1) //Source peripheral: Timer2 MR0
|(0 << 11); //Memory to memory trasnfer
NVIC_SetVector(DMA_IRQn, (uint32_t) DMA_IRQHandler);
NVIC_EnableIRQ(DMA_IRQn);
LPC_GPDMACH0->CConfig |= 1; //Enable channel
LPC_TIM2->IR |= 0x0FF; //CLear all timer interrupts
LPC_TIM2->TCR = 0x01; //start Timer
while(!DMATCCOUNT); //Wait till transfer Complete
while(1){
}
}
void DMA_IRQHandler(void){
uint32_t temp;
DMATCCOUNT++;
temp = LPC_GPDMA->IntTCStat;
if(temp){
DMATCCOUNT++;
LPC_GPDMA->IntTCClear |= temp;
}
temp = LPC_GPDMA->IntErrStat;
if(temp){
DMAERRCOUNT++;
LPC_GPDMA->IntErrClr |= temp;
}
return;
}
I expected data on address 0x20004000, but there is nothing.
Hope any of you guys could help me.
On ATmega 32u4 I've got two timers. One is for measuring ticks between interrupts and second which counts 1/60 of number of ticks. I'm not sure I'm allowed what I'm doing and if that is correct. If you could tell me what is wrong I would greatfull.
void setup(){
EIMSK |= (1 << INT2); //INT2 enable
EICRA |= (1 << ISC21) | (1 << ISC20); //INT2 RISING EDGE
DDRD &= ~(1 << PD2); //PD0 (INT0)(3) as input
PORTD &= ~(1 << PD2); //PD0 pulldown
TCCR1B |= (1 << CS11); //prescaler 8
//TIMER3 - 1/60
TCCR3A |= (1 << WGM32); //CTC Mode
TCCR3B |= (1 << CS31); //prescaler 8
TIMSK3 |= (1 << OCIE3A); //compate interrupt enable
sei();
}
ISR(INT2_vect) {
TCNT3 = 0;
OCR3A = TCNT1 / 60;
TCNT1 = 0;
degree = 30;
}
ISR(TIMER3_COMPA_vect) {
Color * currentColor = &bColor;
if (degree == 0)
degree = 59;
else degree--;
if (degree == time.second)
currentColor = &sColor;
else if (degree == time.minute)
currentColor = &mColor;
else if (degree % 5 == 0 && degree / 5 == time.hour)
currentColor = &hColor;
//set pwm
RedReg = currentColor->Red;
GreenReg = currentColor->Green;
BlueReg = currentColor->Blue;
}