STM32 hot to initialize a 1 us-timer? - stm32

How can I choose a right prescaler/ period value, to get 1us counts?
I'm working with STM32F4 Dicovery board and I need to create a 1us counter and don't know, how I can decrease a steps.
I'm using TIM10. Thank you very much for help!
10us counts works:
void 10_microSek_timer_Init() {
GPIO_InitTypeDef GPIO_InitStructure;
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
TIM_OCInitTypeDef TIM_OCInitStructure;
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8;
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;
GPIO_Init(GPIOB, &GPIO_InitStructure);
GPIO_PinAFConfig(GPIOB, GPIO_PinSource8, GPIO_AF_TIM10); // PB8 -> TIM10 Channel 1
// TIM10 Clock enable
RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM10, ENABLE);
// Timer 10 Config PWM
TIM_TimeBaseInitStructure.TIM_Period = 10- 1; //Change Orig 1 Sek: 10000-1; 1ms: 10-1; 10ms: 100-1; 100ms: 1000-1
TIM_TimeBaseInitStructure.TIM_Prescaler = 168-1; //Change Orig 1 Sek: 16800-1
TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;
TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInit(TIM10, &TIM_TimeBaseInitStructure);
TIM_OCStructInit(&TIM_OCInitStructure);
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
TIM_OCInitStructure.TIM_Pulse = 0;
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_Low;
TIM_OC1Init(TIM10, &TIM_OCInitStructure);
TIM_OC1PreloadConfig(TIM10, TIM_OCPreload_Enable);
TIM_ARRPreloadConfig(TIM10, ENABLE);
TIM_Cmd(TIM10, ENABLE);
TIM_CtrlPWMOutputs(TIM10, ENABLE);
TIM10->CCR1 = 5;
}

TIM_TimeBaseInitStructure.TIM_Period = 1;
TIM_TimeBaseInitStructure.TIM_Prescaler = 84-1;
it should work

There are three registers: PSC, CCR and ARR. The PWM frequency is calculated as follows:
PWM_freq = APBx_freq / (PSC+1)(ARR+1)
Duty Cycle = CCR / ARR
Assuming the APBx_freq = 168 MHz, setting PSC = 0 and ARR = 167, gives a PWM with 1us period and maximum possible duty cycle resolution, i.e: 1/167 = 0.59%.
Increasing the ARR, decreases the frequency but increases the duty cycle resolution. This relationship, is unfortunately, nonlinear.

The mistake was, i don't need a PWM-signal, i need a 1us Timer (TIM10->CNT).
PWM-signal was used to debug a frequency.
The right options are:
TIM_TimeBaseInitStructure.TIM_Period = 0xFFFF;
TIM_TimeBaseInitStructure.TIM_Prescaler = 84-1;
TIM10->CCR1 = 1<<15;

Related

Reading value from HC SR04 in LM016L

I'm programming STM32F103C6, and I'm using Keil Microvision with Proteus Professional 8, I have problem with reading value from HC SR04 Ultrasonic sensor in LCD(LM016L), the code is below
#define TRIG_PIN GPIO_PIN_9
#define TRIG_PORT GPIOA
#define ECHO_PIN GPIO_PIN_8
#define ECHO_PORT GPIOA
uint32_t pMillis;
uint32_t Value1 = 0;
uint32_t Value2 = 0;
uint16_t Distance = 0; // cm
uint16_t temp = 50;
/* USER CODE END PV */
/* USER CODE BEGIN 2 */
HAL_TIM_Base_Start(&htim1);
HAL_GPIO_WritePin(TRIG_PORT, TRIG_PIN, GPIO_PIN_RESET); // pull the TRIG pin low
/* USER CODE END 2 */
lcd_init();
char buffer[24];
while (1)
{
HAL_GPIO_WritePin(TRIG_PORT, TRIG_PIN, GPIO_PIN_SET); // pull the TRIG pin HIGH
__HAL_TIM_SET_COUNTER(&htim1, 0);
while (__HAL_TIM_GET_COUNTER (&htim1) < 10); // wait for 10 us
HAL_GPIO_WritePin(TRIG_PORT, TRIG_PIN, GPIO_PIN_RESET); // pull the TRIG pin low
pMillis = HAL_GetTick(); // used this to avoid infinite while loop (for timeout)
// wait for the echo pin to go high
while (!(HAL_GPIO_ReadPin (ECHO_PORT, ECHO_PIN)) && pMillis + 10 > HAL_GetTick());
Value1 = __HAL_TIM_GET_COUNTER (&htim1);
pMillis = HAL_GetTick(); // used this to avoid infinite while loop (for timeout)
// wait for the echo pin to go low
while ((HAL_GPIO_ReadPin (ECHO_PORT, ECHO_PIN)) && pMillis + 50 > HAL_GetTick());
Value2 = __HAL_TIM_GET_COUNTER (&htim1);
Distance = (Value2-Value1)* 0.034/2;
sprintf(buffer, "%u\n", Distance);
lcd_puts(0,0, (int8_t*)(buffer));
HAL_Delay(50);
}
}
I think I got some value 10* -300, does anyone has idea how to read it or another code

MCP79411 RTC connection via i2c/TWI interface on Atmel SAMG55

I've made a project based on ATSAMG55J19 MCU, programmed with Atmel Studio and ASF 3
Now i'm trying to add an external RTC clock, because internal SAMg55 rtc does not have a backup battery.
The module will be used to read current time after power failure, then i'll use internal RTC, so i need only basic communication. No need to write specific data in EEPROM or setting alarms.
I have a MCP79411, connected via i2c, but there aren't any library suitable for this MCU that uses ASF TWI library.
There are many Arduino implementation, but they uses Wire.h library, and i can't port it.
I made an attempt porting this simple "driver": https://www.ccsinfo.com/forum/viewtopic.php?t=54105
Here is some code
static void i2c_start(void){
static twi_options_t ext3_twi_options;
flexcom_enable(FLEXCOM4);
flexcom_set_opmode(FLEXCOM4, FLEXCOM_TWI);
ext3_twi_options.master_clk = sysclk_get_cpu_hz();
ext3_twi_options.speed = 100000;
ext3_twi_options.smbus = 0;
twi_master_init(TWI4, &ext3_twi_options);
}
// Init Real Time Clock
void rtc_Init(void)
{
uint8_t seconds = 0;
i2c_start();
twi_write_byte(TWI4, ADDR_RTCC_WRITE); // WR to RTC
twi_write_byte(TWI4, ADDR_SEC); // REG 0
twi_write_byte(TWI4, ADDR_RTCC_READ); // RD from RTC
seconds = bcd2bin(i2c_read(0)); // Read current "seconds" in rtc
//i2c_stop();
//seconds &= 0x7F;
seconds |= 0x80; //set to 1 bit 7 of seconds(ST) enabling oscillator
delay_us(3);
twi_write_byte(TWI4, ADDR_RTCC_WRITE); // WR to RTC
twi_write_byte(TWI4, ADDR_SEC); // REG 0
twi_write_byte(TWI4, bin2bcd(seconds) | 0x80); // Start oscillator with current "seconds value
twi_write_byte(TWI4, ADDR_RTCC_WRITE); // WR to RTC
twi_write_byte(TWI4, 0x07); // Control Register
twi_write_byte(TWI4, 0x80); // Disable squarewave output pin
//i2c_stop();
}
Then i tried rtc_set_date_time(uint8_t day, uint8_t mth, uint8_t year, uint8_t dow, uint8_t hr, uint8_t min, uint8_t sec)
and
void rtc_get_time(uint8_t &hr, uint8_t &min, uint8_t &sec)
{
twi_write_byte(TWI4, ADDR_RTCC_WRITE);
twi_write_byte(TWI4, 0x00);
twi_write_byte(TWI4, ADDR_RTCC_READ);
sec = bcd2bin(twi_read_byte(TWI4) & 0x7f); //0x7f b01111111
min = bcd2bin(twi_read_byte(TWI4) & 0x7f); //0x7f
hr = bcd2bin(twi_read_byte(TWI4) & 0x3f); //0x3f b00111111
//i2c_stop();
}
But i always get "0" bytes.
i could not understand the correct way to open communication and read bytes from i2c.
The only reference i found is http://asf.atmel.com/docs/latest/sam.drivers.twi.twi_eeprom_example.samg53_xplained_pro/html/index.html but it seems to be a very different type of communication.
What is the correct way to send and receive that bytes via i2c?
I managed to get and set data. I post a draft of library:
#include "asf.h"
#include "conf_board_3in4out.h"
#include "external_rtc.h"
#include <time.h>
//#include <time_utils.h>
twi_packet_t packet_tx, packet_rx;
// helper functions to manipulate BCD and binary to integers
static int bcd2dec(char r_char)
{
MSN = (r_char & 0xF0)/16;
LSN = r_char & 0x0F;
return(10*MSN + LSN);
}
static char msn(char tim)
{
return (tim & 0xF0)/16;
}
static char lsn(char tim)
{
return (tim & 0x0F);
}
#define RTC_ADDR 0x6F // 7 bits
char config_t[10];
char config_2[8];
char tim_read[8];
#define ADDR_SEC 0x00 // address of SECONDS register
#define ADDR_MIN 0x01 // address of MINUTES register
#define ADDR_HOUR 0x02 // address of HOURS register
#define ADDR_DAY 0x03 // address of DAY OF WEEK register
#define ADDR_STAT 0x03 // address of STATUS register
#define ADDR_DATE 0x04 // address of DATE register
#define ADDR_MNTH 0x05 // address of MONTH register
#define ADDR_YEAR 0x06 // address of YEAR register
#define ADDR_CTRL 0x07 // address of CONTROL register
#define ADDR_CAL 0x08 // address of CALIB register
#define ADDR_ULID 0x09 // address of UNLOCK ID register
#define ADDR_SAVtoBAT_MIN 0x18 // address of T_SAVER MIN(VDD->BAT)
#define ADDR_SAVtoBAT_HR 0x19 // address of T_SAVER HR (VDD->BAT)
#define ADDR_SAVtoBAT_DAT 0x1a // address of T_SAVER DAT(VDD->BAT)
#define ADDR_SAVtoBAT_MTH 0x1b // address of T_SAVER MTH(VDD->BAT)
#define START_32KHZ 0x80 // start crystal: ST = b7 (ADDR_SEC)
#define OSCON 0x20 // state of the oscillator(running or not)
#define VBATEN 0x08 // enable battery for back-up
static uint8_t bin2bcd(uint8_t binary_value)
{
uint8_t temp;
uint8_t retval;
temp = binary_value;
retval = 0;
if(temp >= 10)
{
temp -= 10;
retval += 0x10;
}
else
{
retval += temp;
//break;
}
return(retval);
}
static uint8_t bcd2bin(uint8_t bcd_value)
{
uint8_t temp;
temp = bcd_value;
temp >>= 1;
temp &= 0x78;
return(temp + (temp >> 2) + (bcd_value & 0x0f));
}
static void setConfig(void){
config_2[0] = tim_read[0] | START_32KHZ; // bitwise OR sets Start osc bit = 1
config_2[1] = tim_read[1];
config_2[2] = tim_read[2];
//0x03h – Contains the BCD day. The range is 1-7.
//Bit 3 is the VBATEN bit. If this bit is set, the
//internal circuitry is connected to the VBAT pin
//when VCC fails. If this bit is ‘0’ then the VBAT pin is
//disconnected and the only current drain on the
//external battery is the VBAT pin leakage.
config_2[3] = tim_read[3] | VBATEN;
config_2[4] = tim_read[4];
config_2[5] = tim_read[5];
config_2[6] = tim_read[6];
config_2[7] = 0x00; // control b3 - extosc = 0
}
static void initialize(void){
uint8_t buf[7]; // Fill this with RTC clock data for all seven registers
// read stored time data
config_t[0] = 0x00; //reset pointer reg to '00'
// Set up config to read the time and set the control bits
//i2c.write(addr, config_t, 1); // write address 00
packet_tx.chip = RTC_ADDR;
packet_tx.addr[0] = 0; // RTCSEC
packet_tx.addr_length = 1;
packet_tx.buffer = config_t;
packet_tx.length = 1;
twi_master_write(TWI4, &packet_tx);
delay_ms(250);
//
//i2c.read(addr, tim_read, 7); //read time ss mm hh from r1, r2, r3
packet_rx.chip = RTC_ADDR;
packet_rx.addr[0] = 0; // RTCSEC
packet_rx.addr_length = 1;
packet_rx.buffer = tim_read;
packet_rx.length = sizeof(tim_read);
twi_master_read(TWI4, &packet_rx);
delay_ms(250);
setConfig(); //puts RTCC data into config array from tim_read array
// write the config data
//i2c.write(addr, config_t, 9); // write the config data back to the RTCC module
packet_tx.chip = RTC_ADDR;
packet_tx.addr[0] = 0; // RTCSEC
packet_tx.addr_length = 1;
packet_tx.buffer = config_2;
packet_tx.length = sizeof(config_2);
twi_master_write(TWI4, &packet_tx);
}
static void write_time(void){
// re-calculate mins
mins = 8; //ORE 10:08
mins = mins%60;
ch_mins = 16*(mins/10) + mins%10;
MSN = msn(ch_mins);
LSN = lsn(ch_mins);
tim_read[1] = ch_mins;
inc_mins = 0;
//write the data back to RTCC
setConfig();
//i2c.write(addr, config_t, 9);
/* Configure the data packet to be transmitted */
packet_tx.chip = RTC_ADDR;
packet_tx.addr[0] = 0; // RTCSEC
packet_tx.addr_length = 1;
packet_tx.buffer = config_2;
packet_tx.length = sizeof(config_2);
twi_master_write(TWI4, &packet_tx);
//Display and set hours
//hrs = bcd2dec(tim_read[2]);
// re-calculate hrs
hrs = 10; //ORE 10:08
hrs = hrs%24;
ch_hrs = 16*(hrs/10) + hrs%10;
MSN = msn(ch_hrs);
LSN = lsn(ch_hrs);
tim_read[2] = ch_hrs;
inc_hr = 0;
//write the data back to RTCC
setConfig();
/* Configure the data packet to be transmitted */
packet_tx.chip = RTC_ADDR;
packet_tx.addr[0] = 0; // RTCSEC
packet_tx.addr_length = 1;
packet_tx.buffer = config_2;
packet_tx.length = sizeof(config_2);
twi_master_write(TWI4, &packet_tx);
}
static void read_time(void){
//config_t[0] = 0x00; //reset pointer reg to '00'
//// First Get the time
////i2c.write(addr, config_t, 1); // write address 00
//packet_tx.chip = RTC_ADDR;
//packet_tx.addr[0] = 0; // RTCSEC
//packet_tx.addr_length = 1;
//packet_tx.buffer = config_t;
//packet_tx.length = 1;
//
//twi_master_write(TWI4, &packet_tx);
delay_ms(250);
uint8_t buf[7]; // Fill this with RTC clock data for all seven registers
/* Configure the data packet to be received */
packet_rx.chip = RTC_ADDR;
packet_rx.addr[0] = 0; // RTCSEC
packet_rx.addr_length = 1;
packet_rx.buffer = buf;
packet_rx.length = sizeof(buf);
twi_master_read(TWI4, &packet_rx);
for(uint8_t i = 0; i < sizeof(buf); i++){
tim_read[i] = buf[i];
}
}
void example_print_time(void){
//initialize();
delay_ms(1000);
//write_time(); //commented to see if time is permanent
delay_ms(1000);
read_time();
while(1){
printf("Reading time\n");
printf("%d:%d:%d\n", bcd2dec(tim_read[2]), bcd2dec(tim_read[1]), bcd2dec(tim_read[0] ^ START_32KHZ));
delay_ms(1000);
read_time();
}
}

Calibrating STM32 ADC (VREFINT)

I'm trying to read VDDA on an STM32F042 microcontroller. I'm getting unexpected results with VDD at 3.29V. I must be missing something fundamental.
output:
VREFINT=1917; VREFINT_CAL=1524; VDDA=2623 mV
VREFINT=1885; VREFINT_CAL=1524; VDDA=2668 mV
VREFINT=1913; VREFINT_CAL=1524; VDDA=2628 mV
VREFINT=1917; VREFINT_CAL=1524; VDDA=2623 mV
VREFINT=1917; VREFINT_CAL=1524; VDDA=2623 mV
adc_test.c:
#include <stdio.h>
#include "stm32f0xx.h"
#define VREFINT_CAL_ADDR 0x1FFFF7BA /* datasheet p. 19 */
#define VREFINT_CAL ((uint16_t*) VREFINT_CAL_ADDR)
extern void initialise_monitor_handles(void);
int main(void)
{
RCC->APB2ENR |= RCC_APB2ENR_ADC1EN; /* enable ADC peripheral clock */
RCC->CR2 |= RCC_CR2_HSI14ON; /* start ADC HSI */
while (!(RCC->CR2 & RCC_CR2_HSI14RDY)); /* wait for completion */
/* calibration */
ADC1->CR |= ADC_CR_ADCAL; /* start ADc CALibration */
while (ADC1->CR & ADC_CR_ADCAL); /* wait for completion */
ADC1->CR |= ADC_CR_ADEN; /* ADc ENable */
while (!(ADC1->ISR & ADC_ISR_ADRDY)); /* wait for completion */
ADC1->SMPR |= ADC_SMPR1_SMPR_0 | /* sampling mode: longest */
ADC_SMPR1_SMPR_1 |
ADC_SMPR1_SMPR_2;
/* VDD reference */
ADC->CCR |= ADC_CCR_VREFEN; /* VREF Enable */
ADC1->CHSELR = ADC_CHSELR_CHSEL17; /* CH17 = VREFINT */
initialise_monitor_handles(); /* enable semihosting */
while (1) {
ADC1->CR |= ADC_CR_ADSTART; /* start ADC conversion */
while (!(ADC1->ISR & ADC_ISR_EOC)); /* wait for completion */
uint32_t vdda = 3300UL * *VREFINT_CAL / ADC1->DR; /* ref. manual p. 252; constant and result in millivolts */
printf("VREFINT=%lu; VREFINT_CAL=%lu; VDDA=%lu mV\n",
(unsigned long)ADC1->DR,
(unsigned long)*VREFINT_CAL,
(unsigned long)vdda);
}
}
Screenshot from Datasheet:
Screenshot from Reference Manual
note this refers to .3V, but I believe this to be a typo, as the datasheet above and the longer formula below refer to 3.3V, and .3V is below minimum operating voltage for this part
I'm currently developing an ADC driver for STM32L4. During implementation I encounter almost the same problem. In my opinion the first formula
is not calculating the VDDA, but VREF+. It's the voltage against which the ADC is evaluating the ADC-IN channels.
Further the VREFINT_DATA is not measured VREF+ voltage, but an internal reference voltage which is controller dependent. In my case it defined in controller datasheet:
Here's a pic how I am using the posted formulas:
Some comments:
ln 102: calculating VREF+ not VDDA
ln 105-110: calculate all ranks/configured sequence
ln 108: calculate voltage measured by ADCpin_x
ln 109: multiply by gain to get real value
In my opinion by calculating the VREF+ for each conversion sequence, I'll get better results, because so some ripples on VREF+ are compensated.
as #Artur said Vref + is not Vdda, but usually (that's how I have it in my hardware design) Vref + is connected to Vdda (with the corresponding filters according to the datasheet), so calculating Vdda is the same as calculating Vref +.
I will show you how to calculate vdda, as I have it based on the STM32L431.
. You must first configure the ADC to measure VREFINT:
void MX_ADC1_Init(void)
{
/* USER CODE BEGIN ADC1_Init 0 */
/* USER CODE END ADC1_Init 0 */
ADC_ChannelConfTypeDef sConfig = {0};
/* USER CODE BEGIN ADC1_Init 1 */
/* USER CODE END ADC1_Init 1 */
/** Common config
*/
hadc1.Instance = ADC1;
hadc1.Init.ClockPrescaler = ADC_CLOCK_SYNC_PCLK_DIV4;
hadc1.Init.Resolution = ADC_RESOLUTION_12B;
hadc1.Init.DataAlign = ADC_DATAALIGN_RIGHT;
hadc1.Init.ScanConvMode = ADC_SCAN_DISABLE;
hadc1.Init.EOCSelection = ADC_EOC_SINGLE_CONV;
hadc1.Init.LowPowerAutoWait = DISABLE;
hadc1.Init.ContinuousConvMode = DISABLE;
hadc1.Init.NbrOfConversion = 1;
hadc1.Init.DiscontinuousConvMode = DISABLE;
hadc1.Init.ExternalTrigConv = ADC_SOFTWARE_START;
hadc1.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_NONE;
hadc1.Init.DMAContinuousRequests = DISABLE;
hadc1.Init.Overrun = ADC_OVR_DATA_PRESERVED;
hadc1.Init.OversamplingMode = DISABLE;
if (HAL_ADC_Init(&hadc1) != HAL_OK)
{
Error_Handler();
}
/** Configure Regular Channel
*/
sConfig.Channel = ADC_CHANNEL_VREFINT;
sConfig.Rank = ADC_REGULAR_RANK_1;
sConfig.SamplingTime = ADC_SAMPLETIME_247CYCLES_5;
sConfig.SingleDiff = ADC_SINGLE_ENDED;
sConfig.OffsetNumber = ADC_OFFSET_NONE;
sConfig.Offset = 0;
if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK)
{
Error_Handler();
}
/* USER CODE BEGIN ADC1_Init 2 */
/* USER CODE END ADC1_Init 2 */
}
. Now I will show you the code where the equation is executed:
vdda = 3.0 * (VREFINT_CAL /average);
vch = VREF * (average / ADC_RESOLUTION);
log("vdd = %.5f - ", vdda);
log("vchn = %.5f", vch);
where:
#define ADC_RESOLUTION 4095.0 // adc resolution 12 bits
#define VREFINT_CAL 1655.00 // Raw data acquired at a temperature of 30 °C (± 5 °C), VDDA = VREF+ = 3.0 V (± 10 mV)
#define VREF 3.3 // voltage reference 3.3V
note:
'average' is an average of 256 samples taken by the adc (it's just a simple filter).
'log' is a function created by me similar to printf for the uart.
'VREFINT_CAL' varies according to the model.
result:
vdd = 3.28035 - vchn = 1.21343
as we see VREFINT matches the datasheet (1.212V typ.):
VREFINT
Actually it is calculating Vdda, since the Vref calculation is very simple, you have to read the corresponding channel of the ADC with a sample time longer than the one marked in the data sheet (usually 10 us). If Vdda is 2.0 V, a value of 4095 corresponds to 2.0 (or more) V absolute (related GND). In a linear way, the value of Vref will be much higher than if it is read with Vdda = 3.30 V. Therefore, the compensation of the values ​​read with 2.0 V is necessary to know the absolute values ​​of voltage that the ADC is measuring. If they are not compensated, they will be values ​​relative to the voltage level that Vdda has at that moment.
In addition, the power supply value is achieved, which will be useful in order not to go beyond the specifications of the microcontroller.
The answer (big thanks to #jasonharper) is a missing ground connection. Jason't comments on the OP are the best source of wisdom in this thread. I post a summary here so this question can have an accepted answer.
The board went through a number of revisions and in this iteration we forgot to connect the thermal pad, which on this part is the only ground connection. The chip was getting ground through ESD diodes on pins that were connected to ground. It's surprising to me that it worked at all. I was able to increase the current to the chip by configuring grounded GPIOs as outputs and setting them low.

Generating two opposite PWM signals with timers and GPIO/AF pins change

I need to generate two opposite PWM signals (when one is high the other one is low) using timers in STM32. I have read several examples and this is the code I came up with:
void TM_PINS_Init(void) {
GPIO_InitTypeDef GPIO_InitStruct;
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOD, ENABLE);
/* Alternating functions for pins */
GPIO_PinAFConfig(GPIOD, GPIO_PinSource12, GPIO_AF_TIM4);
GPIO_PinAFConfig(GPIOD, GPIO_PinSource13, GPIO_AF_TIM4);
/* Set pins */
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_12 | GPIO_Pin_13;
GPIO_InitStruct.GPIO_OType = GPIO_OType_PP;
GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_NOPULL;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF;
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_100MHz;
GPIO_Init(GPIOD, &GPIO_InitStruct);
}
void TM_TIMER_Init(void) {
TIM_TimeBaseInitTypeDef TIM_BaseStruct;
/* Enable clock */
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4, ENABLE);
uint16_t Period;
Period = 1000000 / 200000; // 200 KHz from 1MHz
TIM_BaseStruct.TIM_Prescaler = (SystemCoreClock / 1000000) - 1; // 1MHz
TIM_BaseStruct.TIM_CounterMode = TIM_CounterMode_Up;
TIM_BaseStruct.TIM_Period = Period - 1;
TIM_BaseStruct.TIM_ClockDivision = 0;
TIM_BaseStruct.TIM_RepetitionCounter = 0;
/* Initialize TIM4 */
TIM_TimeBaseInit(TIM4, &TIM_BaseStruct);
/* Start count on TIM4 */
TIM_Cmd(TIM4, ENABLE);
}
void TM_PWM_Init(void) {
TIM_OCInitTypeDef TIM_OCStruct;
/* PWM mode 2 = Clear on compare match */
/* PWM mode 1 = Set on compare match */
TIM_OCStruct.TIM_OCMode = TIM_OCMode_PWM2;
TIM_OC1Init(TIM4, &TIM_OCStruct);
TIM_OC1PreloadConfig(TIM4, TIM_OCPreload_Enable);
TIM_OCStruct.TIM_OCMode = TIM_OCMode_PWM1;
TIM_OC2Init(TIM4, &TIM_OCStruct);
TIM_OC2PreloadConfig(TIM4, TIM_OCPreload_Enable);
TIM_OCStruct.TIM_OutputState = TIM_OutputState_Enable;
TIM_OCStruct.TIM_OCPolarity = TIM_OCPolarity_Low;
TIM_OCStruct.TIM_Pulse = Period/2; /* 50% duty cycle */
}
I have several questions:
Since I need frequencies in terms of tens of kHz, do I need 100MHz setting for the GPIO speed? Is there any benefit to make it slower?
Would my PWM function generate what I need? I think the opposite PWM signals are actually achieved using PWM modes 1 and 2, but I am afraid I may be missing something there.
I want to be able to stop the timers, and to set high/low state to the same pins. Would the following code work?
GPIOD-> MODER |= (5 << 24); //set pin 12 and 13 to GPIO, 5 gives us 101, shifted 23 bits so that '1's are in pos. 24 and 26
GPIOD->regs->BSRR = (3<<12); // 3 gives us 11 pattern, shifted 12 bits so that pins 12 and 13 are set to high
GPIOD-> MODER |= (5 << 24); //set pin 12 and 13 to AF, 5 gives us 101, shifted 24 bits so that '1's are in pos. 25 and 27
When the AF is restored, are the previous pwm settings restored or must be set again.
Would it be better to use MODER as above to change pins to GPIO and then set to high/low, or I can use 100% duty cycle for high and 0% duty cycle for low?
Q1: No, you do not need to. Faster settings means more power consumed by the peripheral and sharper edges of the generated signal which causes more EMI. If you do not care about the both - it does not matter.
Q2: You need to check it yourself if you use some kind of "magic" numbers.
Q3: Same as above. Why don't you use human readable CMSIS definitions only "magic" functions. If you correct set the register values it will work
Q4: If you stopped the timer you need to start/configure it again. If not it should work.
Q5: It does not matter. But running timer will generate noise and consume some power.

STM32F030F4 does not start I2C1 correctly in Bare Metal

STM32F030F4 does not start I2C1 correctly.
this is my code:
//Clocks------------------------------------------------
RCC->AHBENR = 0x200000;
RCC->APB1ENR = 0x4040 ;
//------------------------------------------------------
//GPIO--------------------------------------------------
//Configure Alternate Fucntion GPIO to I2C1
GPIOA->AFR[1] = 0x440 //(0b0100)<<4 | (0b0100)<<8 //AF4;
//Set GPIOA Pin 9-10 as Alternate Function
GPIOA->MODER = 0x280000;
//Set GPIOA pin 9-10 as speed as High
GPIOA->OSPEEDR = 0x3c0000;
//Set GPIOA pin 9-10 as open drain
GPIOA->OTYPER = 0x600;
//Set GPIOA pin 9-10 as no Pull-up Pull-down
GPIOA->PUPDR = 0x0;
//------------------------------------------------------
//I2C Resgisters
I2C1->TIMINGR = 0x00201D2D;
I2C1->CR1 = 0x1;
I2C1->CR2 = 0x307001c;
I2C1->TXDR = 0x111;
//Start I2C
I2C1->CR2 |= (0b1) << 13;
But output is not true.
It is like this:
SDA port is not working.
This line seems to be problematic.
GPIOA->AFR[1] = GPIO_Aternate_Function_I2C1;
You should set AFSEL9 and AFSEL10 value if I2C pins are 9 and 10. But you set AFSEL8 (which is the lowest in AFR[1]), and clear others.
This code should do the thing:
GPIOA->AFR[1] = GPIO_Aternate_Function_I2C1 * 0x00000110;
Problem Solved..
Clocks are problematic:
changes:(
//Enable PORTA clocks
RCC->AHBENR |= 0x20000;
//Enable I2C1 clocks
RCC->APB1ENR |= 0x200000 ;
)