Analogue input pins PA8, PA11 and PA12 not working on STM32F103RB - c

Working on a simple ADC project reading voltages from a potential divider on a number of channels. I am using the STM32f103rb on a commercial and well made PCB. I am reading pins on PORTA and the following pins work and return a voltage:
PA0, PA1, PA4, PA5, PA6, PA7.
However the following pins do not work and return a raw value of around 2000-2700 approx:
PA8, PA11 and PA12.
The nature of the project and the fact that the PCB is of a fixed design means that we are stuck with these pin selections. The datasheet is quite specific about these pins being usable as AIN. All set-up and config is as per standard STM32, taken from basic example code and modified for our purposes. The included code is a debugging version we made in an attempt to find the cause but to no avail.
Voltage at the pins has been measured and is correct for the type of voltage divider.
Any help would be greatly appreciated on this.
/* Includes ------------------------------------------------------------------*/
#include "main.h"
#include "stdio.h"
#include "stdlib.h"
// Standard STM peripheral config functions
void RCC_Configuration(void);
void GPIO_Configuration(void);
// ADC config - STM example code
void NEW_ADC_Configuration(void);
// Function to read ADC channel and output value
u16 NEW_readADC1(u8 channel);
// Variables
double voltage_su; // Variable to store supply voltage
double voltage7;
double voltage8;
//*****************************************************************************
// Main program
int main(void)
{
// Initialise peripheral modules
RCC_Configuration();
GPIO_Configuration();
NEW_ADC_Configuration();
// Infinate loop
while (1)
{
// Get value of supply voltage and convert
voltage_su = (((3.3 * NEW_readADC1(ADC_Channel_12)) / 4095));
}
}
//*****************************************************************************
// STM RCC config
void RCC_Configuration(void)
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC, ENABLE); // Connect PORTC
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE); // Connect PORTB...
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); // Connect PORTA...
}
//*****************************************************************************
// STM GPIO config
void GPIO_Configuration(void)
{
GPIO_InitTypeDef GPIO_InitStructure; // Value for setting up the pins
// Sup
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
}
//*****************************************************************************
void NEW_ADC_Configuration(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
ADC_InitTypeDef ADC_InitStructure; // Varibale used to setup the ADC
RCC_ADCCLKConfig(RCC_PCLK2_Div4); // Set ADC clock to /4
// Enable ADC 1 so we can use it
RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE);
// Conenct the port A to peripheral clock
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
// Restore to defaults
ADC_DeInit(ADC1);
/* ADC1 Configuration ----------------------------------------------------*/
ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;
// Scan 1 at a time
ADC_InitStructure.ADC_ScanConvMode = DISABLE;
// No continuous conversions - do them on demand
ADC_InitStructure.ADC_ContinuousConvMode = DISABLE;
// Start conversion on software request - not bu external trigger
ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;
// Conversions are 12 bit - put them in the lower 12 bits of the result
ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;
// How many channels are to be sued by the sequencer
ADC_InitStructure.ADC_NbrOfChannel = 1;
// Setup ADC
ADC_Init(ADC1, &ADC_InitStructure);
// Enable ADC 1
ADC_Cmd(ADC1, ENABLE);
// Enable ADC1 reset calibaration register
ADC_ResetCalibration(ADC1);
// Check end of reset calib reg
while(ADC_GetResetCalibrationStatus(ADC1));
//Start ADC1 calib
ADC_StartCalibration(ADC1);
// Check the end of ADC1 calib
while(ADC_GetCalibrationStatus(ADC1));
}
//*****************************************************************************
// Function to return the value of ADC ch
u16 NEW_readADC1(u8 channel)
{
// Config the channel to be sampled
ADC_RegularChannelConfig(ADC1, channel, 1, ADC_SampleTime_239Cycles5);
// Start the conversion
ADC_SoftwareStartConvCmd(ADC1, ENABLE);
// Wait until conversion complete
while(ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC) == RESET);
// Get the conversion value
return ADC_GetConversionValue(ADC1);
}
//*****************************************************************************

I have never worked with this chip, but after looking at the hardware data sheet for 5 minutes I came up with the following naive conclusions:
The chip has 2 ADCs with 8 channels each, a total of 16.
PA0, PA1, PA4, PA5, PA6, PA7 sounds as if they will belong to the first ADC.
I then assume that PA8, PA11 and PA12 belong to the second ADC, called ADC2 by ST.
Your code seems to only ever mention and initialize ADC1. No trace of ADC2.
Maybe these pins don't work because you haven't even initialized or activated the ADC they belong to?

Also, you should check the data sheet for contention due to those pins being used as "Alternate Function".
Typically, they will list what needs to be done/disconnected in order to utilize the pins without contention (e.g. disconnect some resistor)

Related

STM32L152 UART baudrate halfes using HSE

I'm trying to configure the baudrate of USART1 on an STM32L152. When using the external Clock the baudrate is half of what I configured (e.g. 57600 instead of 115200). However, when using the internal HSI everything is correct. The internal is 16 MHz and the external is an 8 MHz crystal that is used to drive the PLL for a 32 MHz system clock.
This is the RCC init code, which is pretty much standard I guess.
int RCC_Configuration(void)
{
/* DISABLE HSI and target clocks prior to clock config */
RCC_HSICmd(DISABLE);
RCC_PLLCmd(DISABLE);
RCC_HSEConfig(RCC_HSE_OFF);
/* Set HSE as sys clock*/
RCC_SYSCLKConfig(RCC_SYSCLKSource_HSE);
/* Enable ADC & SYSCFG clocks */
RCC_APB2Periph_SYSCFG , ENABLE);
/* Allow access to the RTC */
PWR_RTCAccessCmd(ENABLE);
/* Reset RTC Backup Domain */
RCC_RTCResetCmd(ENABLE);
RCC_RTCResetCmd(DISABLE);
/* LSI used as RTC source clock */
/* The RTC Clock may varies due to LSI frequency dispersion. */
/* Enable the LSI OSC */
RCC_LSICmd(ENABLE);
/* Wait until LSE is ready */
while (RCC_GetFlagStatus(RCC_FLAG_LSIRDY) == RESET);
/* Select the RTC Clock Source */
RCC_RTCCLKConfig(RCC_RTCCLKSource_LSI);
/* Enable the RTC */
RCC_RTCCLKCmd(ENABLE);
/* Wait for RTC APB registers synchronisation */
RTC_WaitForSynchro();
// ENABLE HSE
RCC_HSEConfig(RCC_HSE_ON);
ErrorStatus HSEStartUpStatus = RCC_WaitForHSEStartUp();
if(HSEStartUpStatus == SUCCESS)
{
/* 32Mhz = 8Mhz * 12 / 3 */
RCC_PLLConfig(RCC_PLLSource_HSE, RCC_PLLMul_12, RCC_PLLDiv_3);
/* Enable PLL */
RCC_PLLCmd(ENABLE);
/* Wait till PLL is ready */
while(RCC_GetFlagStatus(RCC_FLAG_PLLRDY) == RESET)
{
}
/* Select PLL as system clock source */
RCC_SYSCLKConfig(RCC_SYSCLKSource_PLLCLK);
/* Wait till PLL is used as system clock source */
while(RCC_GetSYSCLKSource() != 0x0C) // 0x0C = PLL
{
}
/* Enable the PWR clock */
RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR, ENABLE);
PWR->CR = PWR_CR_VOS_0; /* Select the Voltage Range 1 (1.8V) */
while((PWR->CSR & PWR_CSR_VOSF) != 0); /* Wait for Voltage Regulator Ready */
/* HCLK = SYSCLK */
RCC_HCLKConfig(RCC_SYSCLK_Div1);
/* PCLK1 = HCLK/2 */
RCC_PCLK1Config(RCC_HCLK_Div2);
/* PCLK2 = HCLK */
RCC_PCLK2Config(RCC_HCLK_Div1);
/* Enable the GPIOs clocks */
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOA | RCC_AHBPeriph_GPIOB | RCC_AHBPeriph_GPIOC| RCC_AHBPeriph_GPIOD| RCC_AHBPeriph_GPIOE| RCC_AHBPeriph_GPIOH, ENABLE);
/* Enable comparator, LCD and PWR mngt clocks */
RCC_APB1PeriphClockCmd(RCC_APB1Periph_COMP | RCC_APB1Periph_LCD | RCC_APB1Periph_PWR,ENABLE);
}
return 0;
}
I'm using STDperiph to configure the UART1, which on this mcu will run on PCLK2. Checked all the init methods and the register contents. The mantissa and fraction of the baudrate register are correctly calculated and should yield the correct baud rate no matter what the PCLK value is.
This is the UART init code:
void usartinit(void)
{
USART_InitTypeDef USART_InitStructure;
GPIO_InitTypeDef GPIO_InitStructure;
USART_InitStructure.USART_BaudRate = 115200 ;
USART_InitStructure.USART_WordLength = USART_WordLength_8b;
USART_InitStructure.USART_StopBits = USART_StopBits_1;
USART_InitStructure.USART_Parity = USART_Parity_No;
USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
/* Enable GPIO clock */
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOA, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);
GPIO_PinAFConfig(USARTx_GPIO, GPIO_PinSource9, GPIO_AF_USART1);
GPIO_PinAFConfig(USARTx_GPIO, GPIO_PinSource10, GPIO_AF_USART1);
/* Configure USART Tx as alternate function push-pull */
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
GPIO_InitStructure.GPIO_Pin = USARTx_TX;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_40MHz;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;
GPIO_Init(USARTx_GPIO, &GPIO_InitStructure);
/* Configure USART Rx as input floating */
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
GPIO_InitStructure.GPIO_Pin = USARTx_RX;
GPIO_Init(USARTx_GPIO, &GPIO_InitStructure);
/* USART configuration */
USART_Init(USART1, &USART_InitStructure);
/* Enable USART */
USART_Cmd(USART1, ENABLE);
}
The only possibility I can think of right now is that the crystal is just 4 MHz, but this is a Nucleo board and the MCO from the attached STLink is used for HSE, which definitely is 8 MHz.
What's the blatant mistake I'm making?
It seems the clock is messed up. Check the HSE_VALUE macro in your code. That might be using default Dev board crystal value. I would suggest to change that value to the crystal you are using. You can use this link to set the clock speed.
I think the previous answer is correct. When using HSI your system clock is 16MHz, whereas when using HSE you system clock is 32MHz.
I suspect HSE value is set to 16MHz.
You could also test this by setting the multiplier to 4 and the divider to 2 so that the system clock is 16MHz when running the HSE.
The HSE value will be in the startup code somewhere.
The value is used by the USART code so that it knows what frequency the USART is being driven with in order to calculate the baud rate.
Ok, finally figured it out. On the Nucleo boards in the default configuration, the HSE clock is connected to the MCO clock output of the STLink Programmer on the board. On multiple of my boards however this clock signal gets distorted so much that the target uC only sees 4 MHz. If I output the HSE on the MCO of the target it produces a 4 MHz square wave with a weird duty cycle of 75%. When probing the MCO input signal with a scope the probe capacitance is sufficent to produce the correct 8 MHz input.
So, I guess don't trust your eval boards... Will now get a few crystals and populate the "real" external clock on those boards.

Square wave generation with STM32 and timer

I'm trying to generate a square wave / quadrature signal ( 2 square wave with 90 degres offset). The board is a STM32F103C8
I'm not focused on the frequency yet, I just want to have a clean quadrature signal.
My code is not complicated at the moment and here are the 2 mains fonctions in order to initialize the timer:
void init_SW()
{
GPIO_InitTypeDef GPIO_InitStruct;
// Step 1: Initialize GPIO as input for rotary encoder
// PB7 (TIM4_CH2) (encoder pin A), PB6 (TIM4_CH1) (encoder pin B)
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_7 | GPIO_Pin_6;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_2MHz;
GPIO_Init(GPIOB, &GPIO_InitStruct);
// Step 2: Setup TIM4 for encoder input
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4, ENABLE);
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
TIM_TimeBaseStructInit (&TIM_TimeBaseStructure);
TIM_TimeBaseStructure.TIM_Period = 3;
TIM_TimeBaseStructure.TIM_Prescaler = 0;
TIM_Cmd(TIM4, ENABLE);
TIM_TimeBaseInit (TIM4, &TIM_TimeBaseStructure);
/*
TIM4->CCR3=0 ;
TIM4->CCR4=(TIM4->ARR+1)/2;
TIM4->CCER;
*/
}
and:
void timer_ccr_init (void)
{
TIM_OCInitTypeDef TIM_OCInitStructure;
/* always initialise local variables before use */
TIM_OCStructInit (&TIM_OCInitStructure);
/* Common settings for all channels */
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_Toggle;
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
/* Channel1 */
TIM_OCInitStructure.TIM_Pulse = 0;
TIM_OC1Init (TIM4, &TIM_OCInitStructure);
/* Channel2 - 90 degres after*/
TIM_OCInitStructure.TIM_Pulse = 1;
TIM_OC2Init (TIM4, &TIM_OCInitStructure);
TIM4->CCER;
}
Do you have any idea where I messed up?
I'm not quite familiar with the F1 line or the Standard Peripheral Library, but I think this is wrong
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_PP;
To connect the timer output signals to the actual pins, you'd rather set them to Alternate Function mode.
This line is at the wrong place:
TIM_Cmd(TIM4, ENABLE);
Enable the timer at the end, when all channels are set up. Because you are not using the prescaler, and the period is quite short, one channel might do a few cycles while you'd be still setting up the other. However, this would be no problem, if you'd enable the channels simultaneously (this is possible at the register level, but STL might not be able to do it), but
TIM4->CCER;
on a line alone has no effect (reads the register and discards the value). You should actually set some bits in this register to enable some channels.
/* Channel2 - 90 degres after*/
TIM_OCInitStructure.TIM_Pulse = 1;
Because the period is 4 cycles (0 to 3), it would result in a 45 degrees offset. Channel 1 toggled in cycle 0, channel 2 in cycle 1, nothing happens in cycles 2 and 3.

STM32F4 SPI issue

I'am using STM32F407vg and i'am trying to write data in SPI data register, the following code shows the configuration function
void init_SPI1(void){
GPIO_InitTypeDef GPIO_InitStruct;
SPI_InitTypeDef SPI_InitStruct;
// enable clock for used IO pins
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);
/* configure pins used by SPI1
* PA4 = NSS
* PA5 = SCK
* PA6 = MISO
* PA7 = MOSI
*/
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_7 | GPIO_Pin_6 | GPIO_Pin_5|GPIO_Pin_4;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF;
GPIO_InitStruct.GPIO_OType = GPIO_OType_PP;
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_NOPULL;
GPIO_Init(GPIOA, &GPIO_InitStruct);
// connect SPI1 pins to SPI alternate function
GPIO_PinAFConfig(GPIOA, GPIO_PinSource4, GPIO_AF_SPI1);
GPIO_PinAFConfig(GPIOA, GPIO_PinSource5, GPIO_AF_SPI1);
GPIO_PinAFConfig(GPIOA, GPIO_PinSource6, GPIO_AF_SPI1);
GPIO_PinAFConfig(GPIOA, GPIO_PinSource7, GPIO_AF_SPI1);
//Set chip select high
GPIOA->BSRRL |= GPIO_Pin_4; // set PE4 high
// enable peripheral clock
RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1, ENABLE);
/* configure SPI1 in Mode 0
* CPOL = 0 --> clock is low when idle
* CPHA = 0 --> data is sampled at the first edge
*/
SPI_StructInit(&SPI_InitStruct); // set default config
SPI_InitStruct.SPI_Direction = SPI_Direction_2Lines_FullDuplex; // set to full duplex mode, seperate MOSI and MISO lines
SPI_InitStruct.SPI_Mode = SPI_Mode_Master; // transmit in master mode, NSS pin has to be always high
SPI_InitStruct.SPI_DataSize = SPI_DataSize_8b; // one packet of data is 8 bits wide
SPI_InitStruct.SPI_CPOL = SPI_CPOL_Low; // clock is low when idle
SPI_InitStruct.SPI_CPHA = SPI_CPHA_1Edge; // data sampled at first edge
SPI_InitStruct.SPI_NSS = SPI_NSS_Soft ; // set the NSS management to internal and pull internal NSS high
SPI_InitStruct.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_4; // SPI frequency is APB2 frequency / 4
SPI_InitStruct.SPI_FirstBit = SPI_FirstBit_MSB;// data is transmitted MSB first
SPI_Init(SPI1, &SPI_InitStruct);
SPI_Cmd(SPI1, ENABLE); // enable SPI1
}
and this is the function that send data
uint8_t SPI1_send(uint8_t data){
SPI1->DR = data; // write data to be transmitted to the SPI data register
while( !(SPI1->SR & SPI_I2S_FLAG_TXE) ); // wait until transmit complete
while( !(SPI1->SR & SPI_I2S_FLAG_RXNE) ); // wait until receive complete
while( SPI1->SR & SPI_I2S_FLAG_BSY ); // wait until SPI is not busy anymore
return SPI1->DR; // return received data from SPI data register
}
I think that i have set the good configuration (the choice of pins is not wrong in my opinion and also the configuration of the bus used ) the following pictures are extracted from the STM32F407 DataSheet
The problem is that the data in the DR registry is different than the data passed as argument .I don't know why this happen .Could anyone guide me to the right point
The SPI DR register is not a normal memory location where writing and reading access the same storage.
Instead, writing loads the output shift register, while reading reads the received input. Depending on device details, reading may also "claim" the input, clearing it from the register until another word is received.
For these reasons, trying to watch the SPI DR with a debugger is not only not going to give you the information you seek, it may even be damaging to the data you would otherwise receive.
After reading the SPI section from this book my problem is solved, i just connect SPI MOSI to SPI MISO pin (PA6 and PA7) then used the following function :
int spiReadWrite(SPI_TypeDef* SPIx, uint8_t *rbuf,
const uint8_t *tbuf, int cnt, enum spiSpeed speed)
{
int i;
SPIx->CR1 = (SPIx->CR1 & ~SPI_BaudRatePrescaler_256) |
speeds[speed];
for (i = 0; i < cnt; i++){
if (tbuf) {
SPI_I2S_SendData(SPIx, *tbuf++);
} else {
SPI_I2S_SendData(SPIx, 0xff);
}
while (SPI_I2S_GetFlagStatus(SPIx, SPI_I2S_FLAG_RXNE) == RESET);
if (rbuf) {
*rbuf++ = SPI_I2S_ReceiveData(SPIx);
} else {
SPI_I2S_ReceiveData(SPIx);
}
}
return i;
}
by using a debugger i can see that the data sent (from tbuf) are succesfully received (in rbuf)
note that SCLK_freq = APB2_freq / (BaudRatePrescaler + 1)

How to initialize I2C on STM32F0?

Recently I've been trying to get the I2C bus working on the STM32F030F4P6 MCU, but with little luck.
I'm using an STM32F0 module and have found plenty of resources for the STM32F1 module I2C initialization, but nothing specific about the STM32F0 initialization/transfer process.
Here's my initialization code:
void i2c_init(uint8_t ownAddress)
{
GPIO_InitTypeDef GPIO_InitStructure;
I2C_InitTypeDef I2C_InitStructure;
// Enable GPIOA clocks
RCC_APB2PeriphClockCmd(RCC_AHBPeriph_GPIOA, ENABLE);
// Configure I2C1 clock and GPIO
GPIO_StructInit(&GPIO_InitStructure);
/* I2C1 clock enable */
RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C1, ENABLE);
/* I2C1 SDA and SCL configuration */
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9 | GPIO_Pin_10;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
GPIO_InitStructure.GPIO_OType = GPIO_OType_OD;
GPIO_Init(GPIOA, &GPIO_InitStructure);
/* I2C1 Reset */
RCC_APB1PeriphResetCmd(RCC_APB1Periph_I2C1, ENABLE);
RCC_APB1PeriphResetCmd(RCC_APB1Periph_I2C1, DISABLE);
/* Configure I2C1 */
I2C_InitStructure.I2C_AnalogFilter = I2C_AnalogFilter_Enable;
I2C_StructInit(&I2C_InitStructure);
I2C_InitStructure.I2C_Mode = I2C_Mode_I2C;
//I2C_InitStructure.I2C_DutyCycle = I2C_DutyCycle_2;
I2C_InitStructure.I2C_OwnAddress1 = ownAddress;
I2C_InitStructure.I2C_Ack = I2C_Ack_Enable;
I2C_InitStructure.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit;
//I2C_InitStructure.I2C_ClockSpeed = ClockSpeed;
I2C_Init(I2C1, &I2C_InitStructure);
I2C_Cmd(I2C1, ENABLE);
}
In order to test to see if my setup was correct I designed some I2C transmission code that would transfer data in a never ending loop. Here's the code for that:
while(1)
{
I2C_SlaveAddressConfig(I2C1, RegName);
I2C_GenerateSTART(I2C1, ENABLE);
I2C_NumberOfBytesConfig(I2C1, 8);
I2C_SendData(I2C1,0b00000000);
I2C_GenerateSTOP(I2C1, ENABLE);
}
Where:
RegName = 0x75
SDA = GPIO_PIN_10 on GPIOA
SCL = GPIO_PIN_9 on GPIOA
I2C = I2C1
ownAdrress = 0x68
When I scope the I2C lines after I start this code I get a floating voltage around 160mV. When I step through the code every one of the I2C function calls happen and complete, so that's why I was thinking that it had something more so to do with my initialization of the pins themselves.
My problem is very similar to this thread, but was never answered:
https://my.st.com/public/STe2ecommunities/mcu/Lists/cortex_mx_stm32/Flat.aspx?RootFolder=https%3a%2f%2fmy%2est%2ecom%2fpublic%2fSTe2ecommunities%2fmcu%2fLists%2fcortex_mx_stm32%2fSTM32F0%20I2C%20code%20doesn%27t%20work&FolderCTID=0x01200200770978C69A1141439FE559EB459D7580009C4E14902C3CDE46A77F0FFD06506F5B&currentviews=1342
Edit 1: Here's my latest code; still not working (Edit 2: Updated to what I currently have; moved things into more correct locations):
void i2c_init(uint8_t ownAddress)
{
/* TypeDefs for GPIOA and I2C1 */
GPIO_InitTypeDef GPIO_InitStructure;
I2C_InitTypeDef I2C_InitStructure;
/* Enable GPIOA clocks and I2C1 clock enable */
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOA, ENABLE);
RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C1, ENABLE);
/* Configure I2C1 clock and GPIO */
GPIO_StructInit(&GPIO_InitStructure);
/* I2C1 SDA and SCL configuration */
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9 | GPIO_Pin_10;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL; //UP
GPIO_InitStructure.GPIO_OType = GPIO_OType_OD;
/* GPIO AF Configuration -> GPIO_AF_1: USART2, CEC, Tim3, USART1, USART2,EVENTOUT, I2C1, I2C2, TIM15 */
GPIO_PinAFConfig(GPIOA, GPIO_PinSource9, GPIO_AF_1);
GPIO_PinAFConfig(GPIOA, GPIO_PinSource10, GPIO_AF_1);
GPIO_Init(GPIOA, &GPIO_InitStructure);
/* I2C1 Reset */
RCC_APB1PeriphResetCmd(RCC_APB1Periph_I2C1, ENABLE);
RCC_APB1PeriphResetCmd(RCC_APB1Periph_I2C1, DISABLE);
//I2C_DeInit(I2C1);
/* Configure I2C1 */
I2C_StructInit(&I2C_InitStructure);
I2C_InitStructure.I2C_AnalogFilter = I2C_AnalogFilter_Enable;
I2C_InitStructure.I2C_Mode = I2C_Mode_I2C;
I2C_InitStructure.I2C_OwnAddress1 = ownAddress;
I2C_InitStructure.I2C_Ack = I2C_Ack_Enable;
I2C_InitStructure.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit;
I2C_Init(I2C1, &I2C_InitStructure);
I2C_Cmd(I2C1, ENABLE);
//I2C_AcknowledgeConfig(I2C1, ENABLE);
}
As you can see I added the two lines GPIO_PinAFConfig(GPIOA, GPIO_PinSource9, GPIO_AF_1); and GPIO_PinAFConfig(GPIOA, GPIO_PinSource10, GPIO_AF_1); where I found that GPIO_AF_1 had the alternate function of I2C1 from the STM32F0 Standard Peripheral bibliotheek.
Any other ideas? I've been playing around with the clocks to see if that changed anything and have been adding snippets of other people's code just to see if that has any effect on the output of my device.
Edit 3: I have tried pulling both the SDA and SCL lines up to VCC with a 1kohm resistor as instructed by this guide: https://electronics.stackexchange.com/questions/57121/i2c-pullup-resistor-calculations
->(3.3V-0.4)/3mA ~= 1kohm
Edit 4: After going through my code line by line I tried outputting various flag bit registers. Specifically these registers: isr = I2C1->ISR;, cr1 = I2C1->CR1;, and cr2 = I2C1->CR2;
The flag I get after initiating the I2C transfer handling with I2C_TransferHandling(I2C1, 0x02, 1, I2C_AutoEnd_Mode, I2C_Generate_Start_Write); was 0x8001 which can be deciphered down to two errors:
#define I2C_ISR_BUSY ((uint32_t)0x00008000) /*!< Bus busy */
and
#define I2C_ISR_TXE ((uint32_t)0x00000001) /*!< Transmit data register empty */
I've found some work arounds at this link here (remove the space after https: to go to the link -> stack overflow won't let me post more than 1 link for some reason): https: //my.st.com/public/STe2ecommunities/mcu/Lists/cortex_mx_stm32/Flat.aspx?RootFolder=%2Fpublic%2FSTe2ecommunities%2Fmcu%2FLists%2Fcortex_mx_stm32%2FSTM32L151RB%20I2C%20Busy%20flag%20always%20set&FolderCTID=0x01200200770978C69A1141439FE559EB459D7580009C4E14902C3CDE46A77F0FFD06506F5B&currentviews=690 that I'm looking to implement and will report back as soon as I try them out.
I have I2C working as master and slave on the F0. The main issue I can see with your code is that in master mode you absolutely must set the I2C_Timing initialisation structure member. See RM0091 for sample values that correspond to the frequency you want to generate on SCL. In slave mode the clock is recovered from the master so the timing member doesn't appear to be used.
As the others have said, external pullups to Vcc on SCL and SDA are not optional and must be present once per bus not per peripheral as you incorrectly stated in a comment. You were right to use the calculator to choose suitable values because the internal pullups in the STM32 at about 30-50K are far too weak for use as I2C pullups.
The problem is that you don't configure proper AF (Alternate Function) source for I2C pins. After reset registers that configure AF are all 0, and I2C's function is 1, so your I2C peripheral is in fact disconnected from the GPIOs.
Technically it makes no difference when you configure that, but it's best to do that before GPIO configuration to minimize any unwanted transitions on the pins.
Edit: You MUST have the pullups on the I2C pins - without them you have "low" level there, and I2C peripheral detects that as bus error, which obviously prevents it from working properly. Either connect external resistors, or at least enable internal pullups instead of "no pullups/no pulldowns" configuration.
Moreover - it's just not possible for an "open drain" pin to work properly without pullup.
The Core of Cortex M0 is different then Core of Cortex M3, STM32f030f4 has AHB and APB1 (bridged) only. The C code of STM32f1xx never be run under STM32f0xx.
Use STM32F0xx_Snippets_Package for solve your key problems.
I just noticed this too:
RCC_APB1PeriphResetCmd(RCC_APB1Periph_I2C1, ENABLE);
RCC_APB1PeriphResetCmd(RCC_APB1Periph_I2C1, DISABLE);
So you have disabled the clock after you enabled it. I don't see where you have re-enabled it later.
I notice that you are mapping the pins to AF_1 and that is for the UART function.
GPIO_PinAFConfig(GPIOA, GPIO_PinSource9, GPIO_AF_1);
GPIO_PinAFConfig(GPIOA, GPIO_PinSource10, GPIO_AF_1);
You need to map them to AF_4 to use the I2C function.
GPIO_PinAFConfig(GPIOA, GPIO_PinSource9, GPIO_AF_4);
GPIO_PinAFConfig(GPIOA, GPIO_PinSource10, GPIO_AF_4);

STM32F4 SPI Configuration SPI_Init causes assert_failed loop

I'm trying to configure SPI to work with a ST7565 GLCD library which is given here. For now, I'm trying to use SPI1 to achieve this. When the init function -which is given below- is called in main(), it causes the program to loop in assert_failed function.
void init_SPI1(void){
GPIO_InitTypeDef GPIO_InitStruct;
SPI_InitTypeDef SPI_InitStruct;
// enable clock for used IO pins
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);
/* configure pins used by SPI1
* PA5 = SCK
* PA6 = MISO
* PA7 = MOSI
*/
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_7 | GPIO_Pin_6 | GPIO_Pin_5;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF;
GPIO_InitStruct.GPIO_OType = GPIO_OType_PP;
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_NOPULL;
GPIO_Init(GPIOA, &GPIO_InitStruct);
// connect SPI1 pins to SPI alternate function
GPIO_PinAFConfig(GPIOA, GPIO_PinSource5, GPIO_AF_SPI1);
GPIO_PinAFConfig(GPIOA, GPIO_PinSource6, GPIO_AF_SPI1);
GPIO_PinAFConfig(GPIOA, GPIO_PinSource7, GPIO_AF_SPI1);
// enable clock for used IO pins
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOE, ENABLE);
/* Configure the chip select pin
in this case we will use PE7 */
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_7;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_OUT;
GPIO_InitStruct.GPIO_OType = GPIO_OType_PP;
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_UP;
GPIO_Init(GPIOE, &GPIO_InitStruct);
GPIOE->BSRRL |= GPIO_Pin_7; // set PE7 high
// enable peripheral clock
RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1, ENABLE);
/* configure SPI1 in Mode 0
* CPOL = 0 --> clock is low when idle
* CPHA = 0 --> data is sampled at the first edge
*/
SPI_InitStruct.SPI_Direction = SPI_Direction_1Line_Tx; // set to full duplex mode, seperate MOSI and MISO lines
SPI_InitStruct.SPI_Mode = SPI_Mode_Master; // transmit in master mode, NSS pin has to be always high
SPI_InitStruct.SPI_DataSize = SPI_DataSize_8b; // one packet of data is 8 bits wide
SPI_InitStruct.SPI_CPOL = SPI_CPOL_Low; // clock is low when idle
SPI_InitStruct.SPI_CPHA = SPI_CPHA_1Edge; // data sampled at first edge
SPI_InitStruct.SPI_NSS = SPI_NSS_Soft; // set the NSS management to internal and pull internal NSS high
SPI_InitStruct.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_16; // SPI frequency is APB2 frequency / 4
SPI_InitStruct.SPI_FirstBit = SPI_FirstBit_MSB;// data is transmitted MSB first
SPI_Init(SPI1, &SPI_InitStruct);
SPI_Cmd(SPI1, ENABLE); // enable SPI1
}
I've noticed that program goes in infinite loop inside assert_failed function when it reaches SPI_Init() line:
SPI_Init(SPI1, &SPI_InitStruct);
The assert_failed function ( Default in Firmware Library) is below:
void assert_failed(uint8_t* file, uint32_t line)
{
/* User can add his own implementation to report the file name and line number,
ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */
/* Infinite loop */
while (1)
{
}
}
I don't know what does it supposed to mean that it loops in assert_failed function. Is it a problem to do with the SPI configuration. I need guidance to understand the problem and generate a solution. Any help will be greately appreciated. Thanks in advance.
EDIT: I've checked inside of the SPI_Init function in stm32f4xx_spi.c
void SPI_Init(SPI_TypeDef* SPIx, SPI_InitTypeDef* SPI_InitStruct)
{
uint16_t tmpreg = 0;
/* check the parameters */
assert_param(IS_SPI_ALL_PERIPH(SPIx));
/* Check the SPI parameters */
assert_param(IS_SPI_DIRECTION_MODE(SPI_InitStruct->SPI_Direction));
assert_param(IS_SPI_MODE(SPI_InitStruct->SPI_Mode));
assert_param(IS_SPI_DATASIZE(SPI_InitStruct->SPI_DataSize));
assert_param(IS_SPI_CPOL(SPI_InitStruct->SPI_CPOL));
assert_param(IS_SPI_CPHA(SPI_InitStruct->SPI_CPHA));
assert_param(IS_SPI_NSS(SPI_InitStruct->SPI_NSS));
assert_param(IS_SPI_BAUDRATE_PRESCALER(SPI_InitStruct->SPI_BaudRatePrescaler));
assert_param(IS_SPI_FIRST_BIT(SPI_InitStruct->SPI_FirstBit));
assert_param(IS_SPI_CRC_POLYNOMIAL(SPI_InitStruct->SPI_CRCPolynomial));
Since the library is locked, I cant get to type anything inside to debug in Live Watch. (I'm using IAR EWARM)
It loops in assert() because the assert failed, so the loop is there to stop further execution.
Just step up on your stack so that you can see which assert in the peripheral library it was that failed. The library does pretty extensive validation of its parameters, so probably something is wrong in one of your calls.
UPDATE It seems you never initialize the CRCPolynomial field, but it's asserted upon. I suggest adding a call to SPI_StructInit() to make sure your init struct is sanely initialized, before you start setting it up according to your application's wishes.

Resources