I am using a STM32F415RGT6 embedded in the 1Bitsy Board. I want to set up the I2C Peripheral in order to read some data from a sensor. I am using the stm32f4 standard peripheral library.
My example code:
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE);
GPIO_InitTypeDef gpioInit;
GPIO_StructInit(&gpioInit);
gpioInit.GPIO_Mode = GPIO_Mode_AF;
gpioInit.GPIO_Pin = GPIO_Pin_6 | GPIO_Pin_7;
gpioInit.GPIO_PuPd = GPIO_PuPd_UP;
gpioInit.GPIO_Speed = GPIO_Speed_25MHz;
GPIO_PinAFConfig(GPIOB, GPIO_PinSource6, GPIO_AF_I2C1);
GPIO_PinAFConfig(GPIOB, GPIO_PinSource7, GPIO_AF_I2C1);
GPIO_Init(GPIOB, &gpioInit);
RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C1, ENABLE);
I2C_DeInit(I2C1);
I2C_InitTypeDef I2C_InitStructure;
I2C_StructInit(&I2C_InitStructure);
/* I2C configuration */
I2C_InitStructure.I2C_Mode = I2C_Mode_I2C;
I2C_InitStructure.I2C_ClockSpeed = 100000;
I2C_InitStructure.I2C_DutyCycle = I2C_DutyCycle_2;
I2C_InitStructure.I2C_OwnAddress1 = 0x01;
I2C_InitStructure.I2C_Ack = I2C_Ack_Enable;
I2C_InitStructure.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit;
I2C_Init(I2C1, &I2C_InitStructure);
I2C_Cmd(I2C1, ENABLE);
while (I2C_GetFlagStatus(I2C1, I2C_FLAG_BUSY));
/* Generate Start, send the address and wait for ACK */
I2C_GenerateSTART(I2C1, ENABLE);
while (!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_MODE_SELECT));
I2C_Send7bitAddress(I2C1, 0xE0, I2C_Direction_Transmitter);
while (!I2C_CheckEvent(I2C1,I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED));
After that I want to write a 0x00, but the code always hangs in the last line, apparently the Master never reads the acknowledge. The I2C status registers always read:
I2C1 -> SR1 = 1024
I2C1 -> SR2 = 3
which means that the Acknowledge Failure bit is always set. If I analyze it using my Saleae I get the following:
The Slave sends the ACK, but the STM32F415 cannot read it.
The weird thing: If I try the same code on my F407 - Disco (only with clock set to 400khz, but it's the same behaviour on both MCUs regardless of Speed), it works flawlessly:
All other peripherals work fine. I already tried several workarounds, but the AF bit is always set, regardless of method. I hope you can help me.
P.S: I have tried with and without additional pullups and the I2C Slave Address is fine, because it works with STM32F0, STM32F4-DISCO and Atmel Mcus.
Best Regards and Thanks in advance!
Related
I try original O-Scope project (PigOScope) without touchscreen based on STM32F103C8T6 Bluepill board, but got some problem:
I used newest rogerclarkmelbourne/Arduino_STM32 Core and downloaded
pingumacpenguin/STM32-O-Scope sketch. I compiled and uploaded it to the device via UART from 0x08000000 address. Then I started the device. The grid and coordinate lines were displayed on the screen. Also on the screen were displayed inscriptions below 0.0 uS/Sample etc... But any noise or pulse signal from PB1 on my Probe. Why the chart is not drawn?
Also I tried to log my steps in Usart in DMA activation code function:
void takeSamples()
{
// This loop uses dual interleaved mode to get the best performance out of
the ADCs
Serial.println("Init DMA");
dma_init(DMA1);
dma_attach_interrupt(DMA1, DMA_CH1, DMA1_CH1_Event);
Serial.println("Enable DMA for ADC");
adc_dma_enable(ADC1);
dma_setup_transfer(DMA1, DMA_CH1, &ADC1->regs->DR, DMA_SIZE_32BITS,
dataPoints32, DMA_SIZE_32BITS, (DMA_MINC_MODE |
DMA_TRNS_CMPLT));// Receive buffer
Serial.println("Set DMA transfer");
Serial.println(maxSamples / 2);
dma_set_num_transfers(DMA1, DMA_CH1, maxSamples / 2);
dma1_ch1_Active = 1;
Serial.println("Enable the channel and start the transfer");
dma_enable(DMA1, DMA_CH1); // Enable the channel and start the transfer.
samplingTime = micros();
Serial.println(samplingTime);
while (dma1_ch1_Active); // SOME BUG OR WHAT.... PROGRAM STOP HERE!!!
samplingTime = (micros() - samplingTime);
Serial.println("Disable DMA");
dma_disable(DMA1, DMA_CH1); //End of trasfer, disable DMA and Continuous
mode.
}
Event handler for stop interrupt
static void DMA1_CH1_Event()
{
dma1_ch1_Active = 0;
}
Volatile flag to stop routine
volatile static bool dma1_ch1_Active = 0;
Program keep crushing on while loop i think... And program does't work beyond takeSamples() function.
Why program does't exit the loop?
Examples of writing to a port seem to always use the port number as a constant, eg,
OCR2A = 180;
How do you write to the port when the port is unknown until run time. For example,
int port = (buttonPressed) ? 0x3b : 0x3c;
portWrite( port, 180 );
What I cannot find is the funtion portWrite(). Does something like that exist?
Robert's answer has some imprecise assertions and an incomplete answer.
Writing directly to port registers you can ruin other settings of the port and sometimes cause permanent damage to controller.
Can ruin other settings: true, you have to know what you are doing (for instance what pins are on the port you are manipulating, and know what are the functions you want to keep.
Can cause permanent damage: not really, or better not because of the port manipulation. If you wire a short circuit to ground and then set it as an output to 1, you can damage it whether you are using the port register or the digitalwrite. You have to be careful in both ways.
Now, returning to your problem, the enumeration is one way, but since the PORTB, PORTC, PORTD are just short name for values, you can set a variable and then use it to indirectly access it.
The type for this kind of variable is a volatile pointer to a byte (volatile means that write and read operations cannot be optimized by the compiler, since the value can change even between two operations):
volatile uint8_t *variablePortRegister;
You just have to load it with the address (so with the & symbol) of the register you want to change:
variablePortRegister = &PORTC;
then use the pointer to change the value
PORTC = 0x12;
becomes
(*variablePortRegister) = 0x12;
This is a short example. For it to work, connect a LED with resistor on arduino pin 5 (bit 5 of PORTD). The LED on the board (labeled L) is connected to pin 13 (bit 5 of PORTB).
The sketch will make one of the two leds blink for five times, then switch to the other. Only port manipulation instructions are used, and you can find that the way to read the port is the same as the one to write it.
volatile uint8_t *myportreg;
unsigned long lastTime;
uint8_t counter;
void setup() {
DDRB |= 0x20;
DDRD |= 0x20;
PORTB = 0;
PORTD = 0;
counter = 99; // trigger the register change immediately
}
void loop() {
if (counter >= 10)
{
counter = 0;
if (myportreg == &PORTD)
myportreg = &PORTB;
else
myportreg = &PORTD;
}
if ((millis() - lastTime) > 500)
{
lastTime = millis();
// change bit 5 of register
*myportreg = 0x20 ^ (*myportreg);
counter++;
}
}
EDIT: as Robert pointed out, it's much better to "use" just the pins you need (in this case, bit 5 of port B and D) rather than setting the whole port; this way you minimize the risk of screwing up something else. This edit is already included in the above code, so the code is correct
The port is a bit in one particular register. If you know the register and the position of the port in that particular register you can try this:
register = (1<<port) || register
to set the port to 1 and
register = (1<<port)^-1 && register
to set the port to 0.
Of course, you will need a switch somewhere to determine the register and the bit of the port in the register given the port name.
I am currently using the STM32F4 with the STM32F429ZI Nucleo-144 Board. I am looking to use this microcontroller to evaluate the position of a rotary encoder via a quadrature encoder interface. Looking at the documentation, this is done with the timers. I have the A/B encoder outputs hooked up to PA6 and PC7 on the micro, but I have noticed that the counts appear to be drifting.
During the debugging, I noticed that if I disconnect one of the encoder outputs to the microcontroller and I move the motor, the counts still increment/decrement even though only one of the encoder lines are connected. Since I am counting on both the TI1 and TI2 edges, this should not be happening. If I am reading the below diagram correctly, since one of my lines is held high using the internal pull-up, clock pulses on the other input should be going up/down/up/down and really just cycling between two different counts. However, if I am rotating the encoder, the counts keep incrementing or decrementing depending on the direction.
Why is the encoder count changing with only one encoder input connected? I also have the scope trace attached to prove that only one count is is active, as well as the code.
EDIT: I have also tried changing the polarity from BOTH EDGE to RISING EDGE, with no perceived benefit.
#include "stm32f4xx_hal.h"
#include "encoder_test.h"
GPIO_InitTypeDef GPIO_InitStruct;
TIM_HandleTypeDef Timer_InitStruct;
TIM_Encoder_InitTypeDef Encoder_InitStruct;
void EncoderTest_Init()
{
__HAL_RCC_GPIOA_CLK_ENABLE();
__HAL_RCC_GPIOC_CLK_ENABLE();
__HAL_RCC_TIM3_CLK_ENABLE();
/**TIM3 GPIO Configuration
PA6 ------> TIM3_CH1
PC7 ------> TIM3_CH2
*/
GPIO_InitStruct.Pin = GPIO_PIN_6;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Pull = GPIO_PULLUP;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_MEDIUM;
GPIO_InitStruct.Alternate = GPIO_AF2_TIM3;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
GPIO_InitStruct.Pin = GPIO_PIN_7;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Pull = GPIO_PULLUP;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_MEDIUM;
GPIO_InitStruct.Alternate = GPIO_AF2_TIM3;
HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);
Timer_InitStruct.Instance = TIM3;
Timer_InitStruct.Init.Period = 0xFFFF;
Timer_InitStruct.Init.CounterMode = TIM_COUNTERMODE_UP;
Timer_InitStruct.Init.Prescaler = 1;
Timer_InitStruct.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
Encoder_InitStruct.EncoderMode = TIM_ENCODERMODE_TI12;
Encoder_InitStruct.IC1Filter = 0x00;
Encoder_InitStruct.IC1Polarity = TIM_INPUTCHANNELPOLARITY_BOTHEDGE;
Encoder_InitStruct.IC1Prescaler = TIM_ICPSC_DIV1;
Encoder_InitStruct.IC1Selection = TIM_ICSELECTION_DIRECTTI;
Encoder_InitStruct.IC2Filter = 0x00;
Encoder_InitStruct.IC2Polarity = TIM_INPUTCHANNELPOLARITY_BOTHEDGE;
Encoder_InitStruct.IC2Prescaler = TIM_ICPSC_DIV1;
Encoder_InitStruct.IC2Selection = TIM_ICSELECTION_DIRECTTI;
if (HAL_TIM_Encoder_Init(&Timer_InitStruct, &Encoder_InitStruct) != HAL_OK)
{
while (1);
}
if (HAL_TIM_Encoder_Start_IT(&Timer_InitStruct, TIM_CHANNEL_1) != HAL_OK)
{
while (1);
}
}
void TIM3_IRQHandler()
{
HAL_TIM_IRQHandler(&Timer_InitStruct);
}
Upon further investigation, it appears that the issue is due to the prescaler. The prescaler does not work in encoder mode when you provide even values. Since prescaler is the entered value + 1, using the STM32F4 HAL, the entered prescaler must be even.
I found confirmation that I am not the only person with this issue at this forum post. There is some discussion at the post that prescalers may not be compatible with encoder mode, but this has not yet been confirmed. I have sent an email to ST to get to the bottom of it. It is safe to enter a prescaler value of 0 if it is not supported.
Here is the working code below:
#include "stm32f4xx_hal.h"
#include "encoder_test.h"
GPIO_InitTypeDef GPIO_InitStruct;
TIM_HandleTypeDef Timer3_InitStruct;
TIM_Encoder_InitTypeDef EncoderTim3_InitStruct;
void EncoderTest_Init_Tim3()
{
__HAL_RCC_GPIOA_CLK_ENABLE();
__HAL_RCC_GPIOC_CLK_ENABLE();
__HAL_RCC_TIM3_CLK_ENABLE();
/**TIM3 GPIO Configuration
PA6 ------> TIM3_CH1
PC7 ------> TIM3_CH2
*/
GPIO_InitStruct.Pin = GPIO_PIN_6;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Pull = GPIO_PULLDOWN;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_MEDIUM;
GPIO_InitStruct.Alternate = GPIO_AF2_TIM3;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
GPIO_InitStruct.Pin = GPIO_PIN_7;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Pull = GPIO_PULLDOWN;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_MEDIUM;
GPIO_InitStruct.Alternate = GPIO_AF2_TIM3;
HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);
Timer3_InitStruct.Instance = TIM3;
Timer3_InitStruct.Init.Period = 0xFFFF;
Timer3_InitStruct.Init.CounterMode = TIM_COUNTERMODE_UP;
Timer3_InitStruct.Init.Prescaler = 10;
Timer3_InitStruct.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
EncoderTim3_InitStruct.EncoderMode = TIM_ENCODERMODE_TI12;
EncoderTim3_InitStruct.IC1Filter = 0x00;
EncoderTim3_InitStruct.IC1Polarity = TIM_INPUTCHANNELPOLARITY_RISING;
EncoderTim3_InitStruct.IC1Prescaler = TIM_ICPSC_DIV4;
EncoderTim3_InitStruct.IC1Selection = TIM_ICSELECTION_DIRECTTI;
EncoderTim3_InitStruct.IC2Filter = 0x00;
EncoderTim3_InitStruct.IC2Polarity = TIM_INPUTCHANNELPOLARITY_RISING;
EncoderTim3_InitStruct.IC2Prescaler = TIM_ICPSC_DIV4;
EncoderTim3_InitStruct.IC2Selection = TIM_ICSELECTION_DIRECTTI;
if (HAL_TIM_Encoder_Init(&Timer3_InitStruct, &EncoderTim3_InitStruct) != HAL_OK)
{
while (1);
}
if (HAL_TIM_Encoder_Start_IT(&Timer3_InitStruct, TIM_CHANNEL_1) != HAL_OK)
{
while (1);
}
}
void TIM3_IRQHandler()
{
HAL_TIM_IRQHandler(&Timer3_InitStruct);
}
EDIT:
After speaking with ST tech support, the encoder interface was not intended to be used with a prescaler value, even OR odd. I have pasted their response below, but even with using a prescaler value that appears to work, it seems possible that the encoder counts drift over time.
Another solution is to use no prescalers, but instead extend the 16 bit value into the 32 bit space using the approach suggested here. I have reprinted the approach here in case the link goes dead:
From user goosen.kobus.001 on 11/19/2013 on ST's forum:
In my experience using an overflow interrupt to scale up an encoder is not reliable, especially when you have high resolution encoders: it happens from time to time that the encoder value will change sign in the instant you enter the interrupt, causing the system to increment the upper word when it should have decremented etc. This is especially true if the encoder is supposed to be stalled at 0, like a servo motor commanded to go to encoder position 0.
The best approach I have found is to do it manually. this is my procedure:
Ensure that the control loop that reads the encoder value is run often, (i.e. that if your encoder is rotating at full speed, the encoder value is still read at least 10-20 times between overflows. For my servo motor application a 1ms loop interval was sufficient.
keep track of the last read encoder value.
divide the current and last encoder value into quadrants (the most significant 2 bits). i.e. pos_now &= 0xC000; pos_last &= 0xC000;
check to see if the encoder has moved from quadrant 0 to quadrant 3 or 3 to 0 in the last step:
4.1 if(pos_now == 0 && pos_last == 0xC000) upper_word++;
4.2 if(pos_now == 0xC000 && pos_last == 0) upper_word--;
this is why I say the encoder read loop needs to be run often; you need to be sure that the value is read often enough that it is impossible to go from quadrant 0->1->2->3 in between reads.
It should also be possible to put this logic in another timer interrupt that runs at say 10kHz. that way you have an encoder value which is always up to date.
ST RESPONSE:
Hi,
I have got the feedback from the design and the architect of the timers.
The encoder interface has been designed to work without prescaler in order to not downgrade the resolution of the encoder.
As you observed, they have confirmed that it cannot work with even prescaler value but only the odd ones.
We have a sub-counter for the prescaler which is mono-directional, so not affected by the counter direction and incremented on every rising edge of the timer clock (without the prescaler).
The counter direction is updated on every rising edge of the timer clock (without the prescaler) but the counter is incremented only while the sub-counter of the prescaler reach the programmed value and according to the value in the direction bit.
So, in one case, the behavior is the same as without prescaler, because the counter is updated with a different direction (every odd clock cycles number), but in the other case, the direction is always the same when the counter is updated and the encoder interface doesn't work correctly.
So you can use the prescaler but with an odd value.
The recommended use case is without prescaler.
Best regards
ST MCU Tech Support
I need to transfer data serially from AT89s52 to Hyperterminal of PC.
For that I made a sample program to print "Hello" on the hyperterminal of my PC by programming the below given code inside 89s52 microcontroller and connecting it to my PC via serial port. Now when I am opening hyperterminal for the respective port, I should be seeing "Hello" printed on the screen multiple times, but what actually I am seeing is some garbage data getting printed.
This is the code I have used.
#include < AT89X52.H>
#include < STDLIB.H>
#include < STDIO.H>
unsigned int i;
void main (void)
{
TMOD = 0x20;
SCON = 0x50;
TH1 = 0xFD;
TL1 = 0xFD;
TR1 = 1;
TI = 1;
P1 = 0;
while (1)
{
puts("Hello\r");
P1 ^= 0x01; /* Toggle P1.0 each time we print */
for(i=0;i<25000;i++);
}
}
In the Hyper terminal I am not getting correct output i.e. Hello. Instead I am seeing some Garbage characters..
Can anybody help on this please..?
Can you See P1 is toggling? I would rather send a single character first and observe what is sending by using an oscilloscope. You should see a digital signal corresponding to the ASCII value of the character being split-out from the TX pin of the micro. Also you can check the baud rate (exact value) by using the scope. If you are convinced that the correct value is sent at the right baud rate it is most likely that you got a bad connection or perhaps the baud rate should slightly be changed.
i'd like to put the SAM3X chip on sleepmode until a character arrives on the serial port. i was thinking of using an ausiliary flag in the Serial interrupt procedure in order to trigger the wake up procedure? what do you think abou? any advice or any other way i should follow or try?
I recommend reviewing .\arduino-1.5.2\hardware\arduino\sam\cores\arduino\UARTClass.cpp as its UARTClass::begin will detail how the Arduino Framework initializes the SAM's Serial IRQ with:
// Configure interrupts
_pUart->UART_IDR = 0xFFFFFFFF;
_pUart->UART_IER = UART_IER_RXRDY | UART_IER_OVRE | UART_IER_FRAME;
// Enable UART interrupt in NVIC
NVIC_EnableIRQ(_dwIrq);
// Enable receiver and transmitter
_pUart->UART_CR = UART_CR_RXEN | UART_CR_TXEN ;
Where you will need to ensure the baud rate generator is not stopped while in sleepmode.
Along with reading the SAM3X data sheet starting with 5.5.3. which leaves the peripheral clocks enabled.
looks like you may be able to insert the wake up into the UARTClass::IrqHandler
void UARTClass::IrqHandler( void )
{
uint32_t status = _pUart->UART_SR;
// Did we receive data ?
if ((status & UART_SR_RXRDY) == UART_SR_RXRDY)
{
// wake up!!!! Not sure if you even need to wakeup, it should from the sleepmode
_rx_buffer->store_char(_pUart->UART_RHR);
}
With regards to sleeping:
\arduino-1.5.2\hardware\arduino\sam\system\libsam\source\pmc.c
Line 972: void pmc_enable_sleepmode(uint8_t uc_type)
Line 988: void pmc_enable_waitmode(void)
Line 1009: void pmc_enable_backupmode(void)
So I would suspect the following:
pmc_enable_sleepmode(WFI);
would put the unit a sleep and the IrqHandler of the UART_SR_RXRDY would wake itself up, without any code change.
One other alternative would be to use the serial pin's IO as to trigger an interrupt.
attachInterrupt(0, EnableSerialRX, CHANGE);
It would though, loose at least the first byte. the trade off is that you can use the lower power modem of pmc_enable_backupmode() rather than sleep