I have been learning to program mentioned PIC32MK1024MCM064 model for quite some time for a big project in the future. I came to I2C communication part which will be key feature in my project, I have not done anything with an I2C protocol in the past on any microcontroller ever, so I am a complete beginner with I2C. For now, my program goal is to write anything to 24LC512 eeprom chip via I2C bus (since I am working on a breadboard, the verification that eeprom was written successfully would be reading the eeprom with my TL866II+ memory programmer and seeing the data I wrote to eeprom via my pic32 code). After examining protocol`s architecture and hours of datasheet reading I came up with some I2C setup code (I know it may not be very efficient, but lets not take it so far right now). And that code seems partially correct. I looked at my SDA and SCL lines via logic analyzer and I saw some casual I2C data packet (using 100kbit/s so 50kHz frequency). However, when I hooked my 24LC512 to the TL866II+ and read it, the memory map was completely empty (TL866II+ is working properly, I verified it in the past). My 24LC512 wiring:
Vdd is at +3.3V
A2 A1 A0 all grounded
WP is directly connected to ground (tried both ground and Vdd, result is the same)
SDA and SCL both pulled up via 5.1k ohm resistors
I am not sure is it even a software problem, but my wiring looks just like the 24LC512 datasheet recommendations, and I am getting an actual I2C data packet on my SDA line at 100 kbit/s or 50 kHz (as you like it). Maybe my addressing is bad or something else? I dont know much about SMbus, should I enable it or disable it? Is there any difference on 100 and 300 ns hold time on SDA after the falling edge of SCL. Is there any other setup secrets of the I2C? I really thank you for any help provided. (All screenshot are down below)
Vilius
// PIC32MK1024MCM064
// DEVCFG3
#pragma config USERID = 0xFFFF // Enter Hexadecimal value (Enter Hexadecimal value)
#pragma config PWMLOCK = OFF // PWM IOxCON lock (PWM IOxCON register writes accesses are not locked or protected)
#pragma config FUSBIDIO2 = ON // USB2 USBID Selection (USBID pin is controlled by the USB2 module)
#pragma config FVBUSIO2 = ON // USB2 VBUSON Selection bit (VBUSON pin is controlled by the USB2 module)
#pragma config PGL1WAY = OFF // Permission Group Lock One Way Configuration bit (Allow multiple reconfigurations)
#pragma config PMDL1WAY = OFF // Peripheral Module Disable Configuration (Allow multiple reconfigurations)
#pragma config IOL1WAY = OFF // Peripheral Pin Select Configuration (Allow multiple reconfigurations)
#pragma config FUSBIDIO1 = ON // USB1 USBID Selection (USBID pin is controlled by the USB1 module)
#pragma config FVBUSIO1 = ON // USB2 VBUSON Selection bit (VBUSON pin is controlled by the USB1 module)
// DEVCFG2
#pragma config FPLLIDIV = DIV_1 // System PLL Input Divider (1x Divider)
#pragma config FPLLRNG = RANGE_BYPASS // System PLL Input Range (Bypass)
#pragma config FPLLICLK = PLL_FRC // System PLL Input Clock Selection (FRC is input to the System PLL)
#pragma config FPLLMULT = MUL_2 // System PLL Multiplier (PLL Multiply by 1)
#pragma config FPLLODIV = DIV_2 // System PLL Output Clock Divider (2x Divider)
#pragma config BORSEL = HIGH // Brown-out trip voltage (BOR trip voltage 2.1v (Non-OPAMP deviced operation))
#pragma config UPLLEN = OFF // USB PLL Enable (USB PLL Disabled)
// DEVCFG1
#pragma config FNOSC = FRC // Oscillator Selection Bits (Internal Fast RC (FRC))
#pragma config DMTINTV = WIN_0 // DMT Count Window Interval (Window/Interval value is zero)
#pragma config FSOSCEN = OFF // Secondary Oscillator Enable (Disable Secondary Oscillator)
#pragma config IESO = OFF // Internal/External Switch Over (Disabled)
#pragma config POSCMOD = OFF // Primary Oscillator Configuration (Primary osc disabled)
#pragma config OSCIOFNC = OFF // CLKO Output Signal Active on the OSCO Pin (Disabled)
#pragma config FCKSM = CSDCMD // Clock Switching and Monitor Selection (Clock Switch Disabled, FSCM Disabled)
#pragma config WDTPS = PS1 // Watchdog Timer Postscaler (1:1)
#pragma config WDTSPGM = STOP // Watchdog Timer Stop During Flash Programming (WDT stops during Flash programming)
#pragma config WINDIS = NORMAL // Watchdog Timer Window Mode (Watchdog Timer is in non-Window mode)
#pragma config FWDTEN = OFF // Watchdog Timer Enable (WDT Disabled)
#pragma config FWDTWINSZ = WINSZ_25 // Watchdog Timer Window Size (Window size is 25%)
#pragma config DMTCNT = DMT31 // Deadman Timer Count Selection (2^31 (2147483648))
#pragma config FDMTEN = OFF // Deadman Timer Enable (Deadman Timer is disabled)
// DEVCFG0
#pragma config DEBUG = OFF // Background Debugger Enable (Debugger is disabled)
#pragma config JTAGEN = OFF // JTAG Enable (JTAG Disabled)
#pragma config ICESEL = ICS_PGx3 // ICE/ICD Comm Channel Select (Communicate on PGEC3/PGED3)
#pragma config TRCEN = OFF // Trace Enable (Trace features in the CPU are disabled)
#pragma config BOOTISA = MIPS32 // Boot ISA Selection (Boot code and Exception code is MIPS32)
#pragma config FECCCON = ECC_DECC_DISABLE_ECCON_WRITABLE// Dynamic Flash ECC Configuration Bits (ECC and Dynamic ECC are disabled (ECCCON<1:0> bits are writable))
#pragma config FSLEEP = OFF // Flash Sleep Mode (Flash is powered down when the device is in Sleep mode)
#pragma config DBGPER = PG_ALL // Debug Mode CPU Access Permission (Allow CPU access to all permission regions)
#pragma config SMCLR = MCLR_NORM // Soft Master Clear Enable (MCLR pin generates a normal system Reset)
#pragma config SOSCGAIN = G3 // Secondary Oscillator Gain Control bits (Gain is G3)
#pragma config SOSCBOOST = ON // Secondary Oscillator Boost Kick Start Enable bit (Boost the kick start of the oscillator)
#pragma config POSCGAIN = G3 // Primary Oscillator Coarse Gain Control bits (Gain Level 3 (highest))
#pragma config POSCBOOST = ON // Primary Oscillator Boost Kick Start Enable bit (Boost the kick start of the oscillator)
#pragma config POSCFGAIN = G3 // Primary Oscillator Fine Gain Control bits (Gain is G3)
#pragma config POSCAGCDLY = AGCRNG_x_25ms// AGC Gain Search Step Settling Time Control (Settling time = 25ms x AGCRNG)
#pragma config POSCAGCRNG = ONE_X // AGC Lock Range bit (Range 1x)
#pragma config POSCAGC = Automatic // Primary Oscillator Gain Control bit (Automatic Gain Control for Oscillator)
#pragma config EJTAGBEN = NORMAL // EJTAG Boot Enable (Normal EJTAG functionality)
// DEVCP
#pragma config CP = OFF // Code Protect (Protection Disabled)
// SEQ
#pragma config TSEQ = 0x0 // Boot Flash True Sequence Number (Enter Hexadecimal value)
#pragma config CSEQ = 0xFFFF // Boot Flash Complement Sequence Number (Enter Hexadecimal value)
//-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
#include <xc.h>
#include <toolchain_specifics.h>
#include <stddef.h>
#include <stdint.h>
#include <stdbool.h>
#include <stdlib.h>
#include "stdio.h"
#include <sys/attribs.h>
//-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
#define CPU_CLOCK_FREQUENCY 8000000
#define _CP0_GET_COUNT() _mfc0 (_CP0_COUNT, _CP0_COUNT_SELECT)
//------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
#define MASTER_WRITE_24LC512_ADDRESS (0B10100000) //Last bit is R/W (1 for reading, and 0 for writing)
#define MASTER_READ_24LC512_ADDRESS (0B10100001) //Last bit is R/W (1 for reading, and 0 for writing)
//------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
void delay_ms ( uint32_t delay_ms)
{
uint32_t startCount, endCount;
endCount=((CPU_CLOCK_FREQUENCY/1000)*delay_ms)/2;
startCount=_CP0_GET_COUNT();
while((_CP0_GET_COUNT()-startCount)<endCount);
}
//-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
void I2C__initialization(void){
//The datasheet declares that when the I2C module is enabled pins are assigned automatically
//(For I2C module nr.1 SCL is pin 5 and SDA is pin 6 ) and TRIS bits are overriden anyway.
//I believe there is nothing else to do with I2C pin setup other than enabling the I2C module
//PIC is operating in master mode, so address and address mask register are irrelevant for this application.
//PB2CLK is same as SYSCLK here (8Mhz)
ANSELG = 0xFFFFFE7F; //Enable digital inputs for I2C pins (Clear ANSEL RG7 and RG8)
I2C1BRG = 0x00000010; //Baud rate setup register (Here 50 kHz for the 100kb/s data rate)
//---------------------------------------------------------------------------
I2C1CON = 0x00000000; //Resetting register to clear all bits (to ensure right setup)
I2C1CONbits.SDAHT = 0b0; //Minimum of 100 ns hold time on SDA after the falling edge of SCL
I2C1CONbits.SIDL = 0b1; //Discontinue module operation when device enters Idle mode
I2C1CONbits.SCLREL = 0b1; //Release SCL clock
I2C1CONbits.DISSLW = 0b1; //Slew rate control is disabled
I2C1CONbits.SMEN = 0b0; //Disable SMBus input thresholds
I2C1CONbits.ON = 0b1; //Enable I2C module
}
//-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
void I2C__state(void){
while( (I2C1CON & 0x0000001F) || (I2C1STAT & 0x00000004) ); //Checking is the I2C bus is idle, waiting until it becomes idle (checking all I2C status bits)
}
//-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
void I2C__Start(void){
I2C__state();
I2C1CONbits.SEN = 0b1; //Initiate Start condition on SDAx and SCLx pins
}
//-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
void I2C__Stop(void){
I2C__state();
I2C1CONbits.PEN = 0b1; //Initiate Stop condition on SDAx and SCLx pins
}
//-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
void I2C_Repeated_Start(void){
I2C__state();
I2C1CONbits.RSEN = 0b1; //Initiate Repeated Start condition on SDAx and SCLx pins
}
//-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
void I2C__Write(uint8_t I2C_data){
I2C__state();
while(I2C1STATbits.TBF != 0); //Ensuring the transmit buffer is completely empty before the new transmission
while(I2C1STATbits.TRSTAT != 0); //Ensuring the transmission has ended
I2C1TRN = I2C_data; //Loading the data to the I2C transmit buffer for transmission
}
//-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
uint8_t I2C__Read (uint8_t ACK){
uint8_t data = 0;
I2C__state();
I2C1CONbits.RCEN = 0b1; //Enables Receive mode for I2C
while(I2C1STATbits.RBF != 0); //Ensuring the receive buffer is completely empty before new reception
data = (I2C1RCV & 0x000000FF); //Extracting lower 8 bits of the I2C data from a 32 bit I2C receive register
I2C1CONbits.ACKEN = ACK;
return data;
}
//-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
int main ( void )
{
TRISCbits.TRISC0 = 1; //Button input setup
ANSELC = 0x00000000; //Enable PORT C digital inputs (VERY IMPORTANT)
TRISAbits.TRISA7 = 0;
LATAbits.LATA7 = 0; //LED 1
TRISBbits.TRISB14 = 0;
LATBbits.LATB14 = 0; //LED 2
TRISBbits.TRISB15 = 0;
LATBbits.LATB15 = 0; //LED 3
TRISGbits.TRISG6 = 0;
LATGbits.LATG6 = 0; //LED 4
TRISGbits.TRISG9 = 0;
LATGbits.LATG9 = 0; //LED 5
TRISAbits.TRISA12 = 0;
LATAbits.LATA12 = 0; //LED 6
TRISAbits.TRISA11 = 0;
LATAbits.LATA11 = 0; //LED 7
I2C__initialization();
while (1)
{
//Background blinking leds
LATBbits.LATB14 = 1;
delay_ms(200);
LATBbits.LATB14 = 0;
LATBbits.LATB15 = 1;
delay_ms(200);
LATBbits.LATB15 = 0;
if(!PORTCbits.RC0){ //Button pressed condition
LATAbits.LATA7 = 1;
I2C__Start();
I2C__Write(MASTER_WRITE_24LC512_ADDRESS);
I2C__Write(0x00); //EEPROM`s address higher 8 bits (random number for testing)
I2C__Write(0x11); //EEPROM`s address lower 8 bits (random number for testing)
I2C__Write(0x49); //Writing random number to the EEPROM memory cell
I2C__Stop();
delay_ms(200);
LATAbits.LATA7 = 0;
}
}
return (EXIT_FAILURE);
}
Related
I am trying to program my bluePill to blink an LED at the PB11 pin while echoing whats being send over the serial port UART1.
as far as my knowledge, Interrupts allows us to run the programme we want and while running it if any flag triggered an interrupt signal the program the control will be diverted to run the interrupt service routine while the original program is running, am I correct here?
So, I'm trying to keep the Green LED blinking and when I type anything on the serial port the red LED blinks while the green is blinking and the blue led must be always turned on unless data is being sent.
Problem 1:
The green led never blinks!
Problem 2:
Whenever sending letters or numbers through Arduino serial monitor the received echo is always 2 letters out of the entire send letters, also the echo is always on the same line, I mean not not on a newline
so typing Hello and pressing enter yields He and when typing hi there and pressing enter the it just adds the first two letters like so Hehi , why?
my code is
/* ********************** Project Description ***********************
STM32f103xx BluePill Board
PB10 = Green LED
PB11 = Blue LED (To be toggled)
PB0 = Red LED (PWM OUTPUT Controlling the Brightness)
PB9 = Push Button for toggling the state of the blue LED
PA0 = Potentiomater Pin (Analog Input)
USART1 Activated and Sends the Voltage of PA0 to the user1
*/
#include "stm32f1xx.h" // Include the MCU header
#include <stdbool.h> // Boolean Library header
#include <stdint.h>
// Prototypes:
void UART1_Init(void); //Enable UART1 on PA9(Tx) & PA10(Rx).
void portsEnable(void); //Enable Clock for desired MCU Ports.
void delay(uint32_t delay); //Intuduce Delays.
void pinsConfig(void); //Configure the used pins.
// Defines and Macros
int main(void)
{
// Inintialization:
portsEnable();
pinsConfig();
UART1_Init(); // Enable USART1 & interrupts # 8Mhz clock Speed only # 9600Bps
while(1)
{
// ******** Blink The green LED ***********************
GPIOB->BSRR = GPIO_BSRR_BS11;
delay(100000);
GPIOB->BSRR = GPIO_BSRR_BR11;
delay(100000);
} // End of while loop
} // End main() loop
// ************** Function Implimentation *****************************
void UART1_Init(void) // Initiallize USART1
{
// Reset Setting (8bit, one stop bit, no parity)
// Enable clock for UART1 First after already enabling the PortA clock
RCC->APB2ENR |= RCC_APB2ENR_USART1EN; // Enable Clock to the USART1 Module
// Pin Configuration for the USART1 Peripheral where Tx(PA9) = AF Push-Pull and Rx = Input Floating
// Setting Tx (PA9) Pin
GPIOA->CRH |= ((1<<4) | (1<<5)); // Set PA9 to Output 50Mhz Mode
GPIOA->CRH &= ~(1<<6); // Configure it to be an AF push-pull
GPIOA->CRH |= (1<<7); // Same as above^
// Setting Rx (PA10) pin
// Nothing to be set as the reset value makes it an input pin with floating configuration
// Set the Baud-Rate
USART1->BRR = 0x341; //# 8Mhz, USARTDIV = 8Mhz/(16*9600) = 52.083 -> 52=0x34 and 0.083 * 16 = 1.328 = 1 which is 0x1
// Enable Interrupts for the USART1 Module:
// A peripheral can generates a flag at a certain event, this flag can trigger an interrupt signal but first the certain event interrupt must be enabled and the peripheral interrupt as well and the global interrupts.
USART1->CR1 |= USART_CR1_TXEIE | USART_CR1_RXNEIE; // Enable the Transmit Data Register Empty Interrupt register and data received interrupt
// Enable the Tx, Rx, USART1 as a whole block
USART1->CR1 |= (USART_CR1_RE | USART_CR1_TE | USART_CR1_UE);
// Enable the USART1 Global interrupt on NVIC "Nested Vectored Interrupt controller" side. The NVIC is the interrupt processing unit in the MCU.
NVIC_EnableIRQ(USART1_IRQn); // This function's name can't be changed!
}
// This is a global interrupt service routine, any flag from the USART1 will lead to the same ISR, to distinguesh which is which we check the the flags and if one is set then this what caused the interrupt
void USART1_IRQHandler(void) // This function's name must be the same as it's defined in the main libraries
{
// Check if we are here because we Received Data or simply the "RXNE flag is set".
if(USART1->SR & USART_SR_RXNE) // If Rx is Not Empty, or if we received Data, The USART1->SR register is going to change as it is controlled by the hardwart and we compare it to the value of the register USART_SR_RXNE which indicates a 1 at bit5
{
char temp = USART1->DR; // Read the 8bit data received fron the data register into a char called temp
USART1->DR = temp; // Put the same data in the data register to be resent again, here the data registers are clled shadow registers they are not the same registers from the hardware prospective but from programming prospective we use the same registers
while(!(USART1->SR & USART_SR_TC))
{
// Wait while the transmission completes and indicate the waiting process by flashing the RED led indicating data being sent.
GPIOB->BSRR = GPIO_BSRR_BR10; // When not ready to accept more data (e.g. while transmitting data process) turn of the Blue LED
GPIOB->BSRR = GPIO_BSRR_BS1;
delay(10000);
GPIOB->BSRR = GPIO_BSRR_BR1;
}
}
// Check if we are here because the TXEIE is set "OR the Transmit complete" meaning we are ready to accept more data to transmit
if(USART1->SR & USART_SR_TXE)
{
// Handle transmit complete here: (Blink an LED)
GPIOB->BSRR = GPIO_BSRR_BS10; // When Ready to accept more data
}
else
{
GPIOB->BSRR = GPIO_BSRR_BR10; // When not ready to accept more data (e.g. while transmitting data process)
}
}
void portsEnable(void) /* Enable PortA and PortB */
{
// Enable clock for Ports (A & B) on "APB2" Bus.
RCC->APB2ENR |= RCC_APB2ENR_IOPAEN; // Enable PortA Clock
RCC->APB2ENR |= RCC_APB2ENR_IOPBEN; // Enable PortB Clock
}
void pinsConfig(void) //Configure the used pins.
{
//Set pin "PB1" as output push-pull (Red LED)
GPIOB->CRL |= ((1<<4) | (1<<5)); //Set Pin to Output 50Mhz max speed
GPIOB->CRL &= ~((1<<6) | (1<<7)); //Configure Pin as Push-Pull
//Set Pin "PB9" as Input Pulled-Up (Push Button Pin)
GPIOB->CRH &= ~(1<<6); //Set PB9 to input "pullup|pulldown"
GPIOB->CRH |= (1<<7);
GPIOB->ODR |= (1<<9); //Set PB9 input pin as Pull-up pin.
//Set pin "PB10" as output push-pull (Blue LED)
GPIOB->CRH |= ((1<<9) | (1<<8)); //Set Pin to Output 50Mhz max speed
GPIOB->CRH &= ~((1<<11) | (1<<10)); //Configure Pin as Push-Pull
//Set pin "PB11" as output push-pull (Green LED)
GPIOB->CRH |= ((1<<12) | (1<<13)); //Set Pin to Output 50Mhz max speed
GPIOB->CRH &= ~((1<<14) | (1<<15)); //Configure Pin as Push-Pull
//Set pin PA0 as Analog input
GPIOA->CRL &= ~((1<<0) | (1<<1)); // Make sure the Mode registers are 00 for input
GPIOA->CRL &= ~((1<<2) | (1<<3)); // Set the CNF registers to 00 for input analog
} // End PinsConfig()
void delay(uint32_t delay) /* Psudo-delay in Milliseconds */
{
for(uint32_t i = 0; i <= delay; i++)
{
// Looping to delay!
}
}
Maybe below link is helpful for you:
stm32 dead lock
I am having troubles in interfacing the ICM20948 IMU with the HAL_layer of ST. My Master device (ST) is sending the data correctly to the slave (ICM) but the slave is not responding. I have checked very well every connection. I have tested several ICM's(3) and I have debugged it with my Logic analyser also (see pic below) I would really appreciate any help or pointer you could provide me.
My current setup is
The code its very simple I just have two simple read/write rotines
// --------------------------------------------------------------------------------------
/// \brief
///
/// \param *imu[in,out]: (IMU_def) IMU object with their respective ports
/// \param reg[in]: (uint8_t) register address
/// \param *pData[out]: (uint8_t) Data buffer
/// \return void
// --------------------------------------------------------------------------------------
void ICM_ReadOneByte (IMU_def *imu, uint8_t reg, uint8_t *pData)
{
reg = reg | 0x80; // Make the MSB active to tell is a read see p31
HAL_GPIO_WritePin(imu->CS_GPIO_Port, imu->CS_Pin, GPIO_PIN_RESET); // Start transmission
HAL_Delay(1);
HAL_SPI_Transmit(imu->spi_bus, ®, 1,1000);
while (HAL_SPI_GetState(imu->spi_bus) != HAL_SPI_STATE_READY) // TODO: Possible inf loop if slave don't answer. Counter?
;
HAL_SPI_Receive(imu->spi_bus, pData, 1,1000);
while (HAL_SPI_GetState(imu->spi_bus) != HAL_SPI_STATE_READY) // TODO: Possible inf loop if slave don't answer. Counter?
;
HAL_GPIO_WritePin(imu->CS_GPIO_Port, imu->CS_Pin, GPIO_PIN_SET); // End transmission
}
// --------------------------------------------------------------------------------------
/// \brief
///
/// \param *imu[in,out]: (IMU_def) IMU object with their respective ports
/// \param reg[in]: (uint8_t) register address
/// \param Data[in]: (uint8_t) Data to write
/// \return void
// --------------------------------------------------------------------------------------
void ICM_WriteOneByte (IMU_def *imu, uint8_t reg, uint8_t Data)
{
reg = reg & 0x7F; // Make the MSB disable to tell is a write see p31
HAL_GPIO_WritePin(imu->CS_GPIO_Port, imu->CS_Pin, GPIO_PIN_RESET); // Start transmission
HAL_Delay(5);
HAL_SPI_Transmit(imu->spi_bus, ®, 1,1000);
HAL_SPI_Transmit(imu->spi_bus, &Data, 1,1000);
HAL_GPIO_WritePin(imu->CS_GPIO_Port, imu->CS_Pin, GPIO_PIN_SET); // End transmission
}
In the main function I use the SPI_HandleTypeDef hspi2; instance to initialise an IMU object as
imu.spi_bus = &hspi2;
imu.CS_GPIO_Port = GPIOB;
imu.CS_Pin = GPIO_PIN_4;
Just for testing that everything is fine I Just selected the BANK_0 and ask WHOIAM by
ICM_WriteOneByte(imu,USER_BANK_SEL, ICM_BANK_0); // USER_BANK_SEL (0x7F); USER_BANK_0:(0x00)
ICM_ReadOneByte(imu, 0x00, &whoami); //
I have also try several steps in between when before calling WHOIAM() such as setting the reseting the device, Seting the internal clock, etc but nothing works. This is how I set other lines just in case:
ICM_WriteOneByte(imu,PWR_MGMNT_1, PWR_MGMNT_1_DEV_RESET); //PWR_MGMNT_1 (0x06);PWR_MGMNT_1_DEV_RESET:(0x80)
ICM_WriteOneByte(imu,PWR_MGMNT_1,PWR_MGMNT_1_CLOCK_SEL_AUTO);//(0x01)
In addition I also try NSS Disable (as is only one device) or Hardware(ST as output) but nothing changed.
Here is a picture of my Logic analyser
As you can see The Master is sending correctly the data to the slave but the slave is always High in the MISO line. I checked the frequency with my logic analyser and is bellow 7MHz as stated in the datashet
Nfalling 8
Nrising 8
fmin 3.846 MHz
fmax 4.167 MHz
fmean 3.977 MHz
Any Ideas are very welcome.
Thanks!
From the manual:
NOTE: To prevent switching into I2C mode when using SPI, the I2C interface should be disabled by setting the I2C_IF_DIS configuration bit. Setting this bit should be performed immediately after waiting for the time specified by the “Start-Up Time for Register Read/Write” in Section 6.3.
I got a STM32 Nucleo-F410RB development board and was able to get my external DAC working with SPI1, both with busy-wait and with DMA. I then designed my own custom PCB, built it and was able to flash it. During the design phase I switched from using SPI1 to SPI5 because I needed the SPI1 pins for other functions. But I couldn't get SPI5 to work in my new design - no signal on the SCK and MOSI pins. When I changed my code to use SPI1, I see signals on the respective SPI1 SCK and MOSI pins.
I went back to my Nucleo board and have the same problem - SPI1 works fine but SPI5 doesn't work at all. I'm using Eclipse with the ARM GNU compiler and the most recent version of the Standard Peripheral Library (not HAL).
SPI init function:
void init_spi(void) {
//initialize MOSI and SCK pins
//initialize SPI
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE);
GPIO_InitTypeDef gpio_init;
gpio_init.GPIO_Pin = GPIO_Pin_0; //SCK
gpio_init.GPIO_Speed = GPIO_Fast_Speed;
gpio_init.GPIO_Mode = GPIO_Mode_AF;
gpio_init.GPIO_OType = GPIO_OType_PP;
gpio_init.GPIO_PuPd = GPIO_PuPd_NOPULL;
GPIO_Init(GPIOB, &gpio_init);
gpio_init.GPIO_Pin = GPIO_Pin_8; //MOSI
gpio_init.GPIO_Speed = GPIO_Fast_Speed;
gpio_init.GPIO_Mode = GPIO_Mode_AF;
gpio_init.GPIO_OType = GPIO_OType_PP;
gpio_init.GPIO_PuPd = GPIO_PuPd_NOPULL;
GPIO_Init(GPIOB, &gpio_init);
GPIO_PinAFConfig(GPIOB, GPIO_PinSource0, GPIO_AF_SPI5);
GPIO_PinAFConfig(GPIOB, GPIO_PinSource8, GPIO_AF_SPI5);
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);
//initialize DAC CS PIN
gpio_init.GPIO_Pin = DAC_CS_PIN;
gpio_init.GPIO_Speed = GPIO_Fast_Speed;
gpio_init.GPIO_Mode = GPIO_Mode_OUT;
gpio_init.GPIO_OType = GPIO_OType_PP;
gpio_init.GPIO_PuPd = GPIO_PuPd_UP;
GPIO_Init(GPIOA, &gpio_init);
SPI_I2S_DeInit(SPI5);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI5, ENABLE); //enable SPI clock
SPI_InitTypeDef spi_init;
spi_init.SPI_Direction = SPI_Direction_1Line_Tx;
spi_init.SPI_Mode = SPI_Mode_Master;
spi_init.SPI_DataSize = SPI_DataSize_8b; //8b? Need to clock in 24 bits of data per DAC channel
spi_init.SPI_CPOL = SPI_CPOL_Low; //5134 uses low to high and high to low clock transitions. ie. idle state is LOW
spi_init.SPI_CPHA = SPI_CPHA_2Edge; //clock phase - data is clocked on falling edge of clock pulse
spi_init.SPI_NSS = SPI_NSS_Soft; //DAC chip select is handled in software
spi_init.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_2; //APB2 clock/2, so 25 MHz SPI clock speed
spi_init.SPI_FirstBit = SPI_FirstBit_MSB; //check datasheet
spi_init.SPI_CRCPolynomial = 7; //what is this?
SPI_Init(SPI5, &spi_init);
SPI_Cmd(SPI5, ENABLE);
}
SPI Write function:
void spi_write_dac(uint16_t value, uint8_t channel) { //currently just use busy/wait to transmit data to test DAC
uint8_t dac_low = value & 0xFF; //take bottom 8 bits
uint8_t dac_high = value >> 8; //take top 8 bits
GPIO_ResetBits(GPIOA, DAC_CS_PIN); //CS low
while (SPI_I2S_GetFlagStatus(SPI5, SPI_I2S_FLAG_TXE) == RESET);//wait for empty buffer
SPI_I2S_SendData(SPI5, channel); //send control byte
while (SPI_I2S_GetFlagStatus(SPI5, SPI_I2S_FLAG_BSY) == SET); //wait for byte to be sent
SPI_I2S_SendData(SPI5, dac_high); //send first data byte
while (SPI_I2S_GetFlagStatus(SPI5, SPI_I2S_FLAG_BSY) == SET); //wait for byte to be sent
SPI_I2S_SendData(SPI5, dac_low); //send second data byte
while (SPI_I2S_GetFlagStatus(SPI5, SPI_I2S_FLAG_BSY) == SET); //wait for byte to be sent
GPIO_SetBits(GPIOA, DAC_CS_PIN);
}
This code does not work but when I change all SPI5 references to SPI1 and use PB3 for SCK and PB5 for MOSI then SPI is working. I've checked the SPI control registers and they look like they are correctly configured for SPI5 so I'm starting to get to my wit's end.
Why will SPI1 work fine on both my own design and on the Nucleo board, but SPI5 will not work on either board?
That is easy answer. SPI5 is not mapped to PB3 and PB5...
If you look at the datasheet on page 39 (datasheet rev 5), you could see that:
On PB3 you can use JTDO-SWO, I2C4_SDA, SPI1_SCK/I2S1_CK, USART1_RX, I2C2_SDA, EVENTOUT, but no SPI5
On PB5, you can use LPTIM1_IN1, I2C1_SMBA, SPI1_MOSI/I2S1_SD, EVENTOUT, but no SPI5
If you really want to use SPI5, you can use the following IOs:
SPI5_MISO: PA12
SPI5_MOSI: PA10 or PB8
SPI5_SCK: PB0
I did the same mistake.
GPIO_PinAFConfig(GPIOB, GPIO_PinSource0, GPIO_AF_SPI5);
GPIO_PinAFConfig(GPIOB, GPIO_PinSource8, GPIO_AF_SPI5);
GPIO_AF_SPI5 must be GPIO_AF6_SPI5 for STM32F410.
#define GPIO_AF6_SPI5 ((uint8_t)0x06) /* SPI5 Alternate Function mapping (Only for STM32F410xx/STM32F411xE Devices) */
Is the SPI_I2S_DeInit(SPI1); normal in your init_spi() function when all your references are for SPI5 peripheral ?
If I am not wrong the target STM32 is a STM32F410RBT6. I let here the Datasheet and Reference Manual for future purposes :
STM32F410RBT6 Datasheet
STM32F410RBT6 Reference Manual
I am using the stm32F0xx series and am trying to get the RTC to work. I have an external 8MHz crystal connected and using PLL to create a sysclk of 48MHz. Obviously I would like to use this clock with the RTC. I have tried the following:
//(1) Write access for RTC registers
//(2) Enable init phase
//(3) Wait until it is allow to modify RTC register values
//(4) set prescaler,
//(5) New time in TR
//(6) Disable init phase
//(7) Disable write access for RTC registers
RTC->WPR = 0xCA; //(1)
RTC->WPR = 0x53; //(1)
RTC->ISR |= RTC_ISR_INIT; //(2)
while ((RTC->ISR & RTC_ISR_INITF) != RTC_ISR_INITF) //(3)
{
//add time out here for a robust application
}
RCC->BDCR = RCC_BDCR_RTCSEL_HSE;
RTC->PRER = 0x007C2E7C; //(4)
RTC->TR = RTC_TR_PM | 0x00000001; //(5)
RTC->ISR &=~ RTC_ISR_INIT; //(6)
RTC->WPR = 0xFE; //(7)
RTC->WPR = 0x64; //(7)
In the main loop there is an infinite for that turns two led's on and off. Without the RTC config this works fine but as soon as I add in the code above it stops working.
If I do this then the rest of the code breaks. Can I use HSE and if so am I using the prescalar correctly?
This example from actual working code for using HSE for RTC at STM32f429. It uses STM HAL software library, but can gives you a clue to solve.
Please note, that HSE already must be configured and used as frequency source before this code.
Remark: when reading, you should read not just time but also date.
i.e.:
HAL_RTC_GetTime(&RTChandle, &RTCtime, FORMAT_BIN); //first
HAL_RTC_GetDate(&RTChandle, &RTCdate, FORMAT_BIN); //second, even if you dont required
otherwise registers stay frozen (in this case you see ticks only under debugger but not in real run, because debug reads both registers)
// enable access to rtc register
HAL_PWR_EnableBkUpAccess();
// 1. 8Mhz oscillator (Source crystal! Not after PLL!) div by 8 = 1 Mhz
__HAL_RCC_RTC_CONFIG(RCC_RTCCLKSOURCE_HSE_DIV8);
RTChandle.Instance = RTC;
RTChandle.Init.HourFormat = RTC_HOURFORMAT_24;
// 2. (1 Mhz / 125) = 7999 ticks per second
RTChandle.Init.AsynchPrediv = 125 - 1;
RTChandle.Init.SynchPrediv = 8000 - 1;
RTChandle.Init.OutPut = RTC_OUTPUT_DISABLE;
RTChandle.Init.OutPutPolarity = RTC_OUTPUT_POLARITY_HIGH;
RTChandle.Init.OutPutType = RTC_OUTPUT_TYPE_OPENDRAIN;
// do init
HAL_RTC_Init(&RTChandle);
// enable hardware
__HAL_RCC_RTC_ENABLE();
Im trying to configure clocks on STM32F4 Discovery for precise time measurement. I have this configuration:
int main(void)
{
NVIC_InitTypeDef nvici;
GPIO_InitTypeDef gpioi;
TIM_TimeBaseInitTypeDef timtbi;
SystemInit();
RCC_HSEConfig(RCC_HSE_ON);
RCC_PLLConfig(RCC_PLLCFGR_PLLSRC_HSE, 8, 320, 8, 8);
RCC_PLLCmd(ENABLE);
RCC_SYSCLKConfig(RCC_SYSCLKSource_PLLCK);
RCC_HCLKConfig(RCC_SYSCLK_Div1);
RCC_PCLK1Config(RCC_HCLK_Div1);
RCC_PCLK2Config(RCC_HCLK_Div1);
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOD, ENABLE);
nvici.NVIC_IRQChannel = TIM2_IRQn;
nvici.NVIC_IRQChannelPreemptionPriority = 0;
nvici.NVIC_IRQChannelSubPriority = 1;
nvici.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&nvici);
gpioi.GPIO_Pin = GPIO_Pin_15;
gpioi.GPIO_Mode = GPIO_Mode_OUT;
gpioi.GPIO_OType = GPIO_OType_PP;
gpioi.GPIO_Speed = GPIO_Speed_100MHz;
gpioi.GPIO_PuPd = GPIO_PuPd_NOPULL;
GPIO_Init(GPIOD,&gpioi);
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
timtbi.TIM_Period = 20000000;
timtbi.TIM_Prescaler = 0;
timtbi.TIM_ClockDivision = 0;
timtbi.TIM_CounterMode = TIM_CounterMode_Up;
timtbi.TIM_RepetitionCounter = 0;
TIM_TimeBaseInit(TIM2, &timtbi);
TIM_ClearITPendingBit(TIM2, TIM_IT_Update);
TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE);
TIM_Cmd(TIM2, ENABLE);
GPIO_SetBits(GPIOD,GPIO_Pin_15);
while(1)
{
}
}
void TIM2_IRQHandler()
{
TIM_ClearITPendingBit(TIM2, TIM_IT_Update);
GPIO_ToggleBits(GPIOD,GPIO_Pin_15);
}
with this i should have TIM2 sourced with 20MHz clock, but it appears to have diffrent frequency (about 10-30% diffrent). This problem appears for all other PLL configurations i tried, but when i use HSE as SYSCLK directly it works just fine. Am i doing something wrong, or is it PLL that isn't reliable?
Can't say with 100% certainty whether that's the problem, but after enabling the HSE using RCC_HSEConfig(), you should call RCC_WaitForHSEStartUp() since it takes a while for the HSE to start oscillating, and check the return code to make sure the call was successful and the HSE actually initialized.
Also, if you're using the system_stm32f4xx.c file that comes with the Standard Peripheral Library, you can scrap your PLL initialization code and just use the code that's called by SystemInit(). There are a few #defines that control the PLL configurations, near the beginning of the file (#define PLL_M, #define PLL_N and so on; their purpose should be self-evident). I always initialize my clocks using the code there, and they're always precise to within the crystal's accuracy. Note that this code assumes a 25 MHz oscillator by using PLL_M equal to 25, so you should set it to 8 for use with the STM32F4DISCOVERY board -- exactly as you've already done in your code. I'm not suggesting this because I have any prejudices against your code, but the code there has been tested far and wide, and in my experience it can be trusted.