I'm trying to enable the LED's on my MCP23S09 by writing to the GPIO register using SPI.
There are two chips on the board one is for the inputs and the other one is for the outputs, so the LED's.
I connected everything like I should, so I took CH2 low and connected the MOSI and SCK pin to my microcontroller.
I'm using a Nucleo STM32F411 in combination with the CubeMX software, So I'm trying to send data to the registers to enable functionality.
But unfortunately none of the LED's lit up on my IO Expander.
Next thing I tried was STM32duino, so I can write Arduino code for my board. But as far as I know this is just another layer on top of the HAL libraries.
To my suprise it worked just fine! It's the same piece of code, I just changed it a bit to work for Arduino.
But I still don't understand why it doesn't work when using the HAL libraries generated by CubeMX.
Arduino Code:
#include <SPI.h>
#define IODIR 0x00
#define IPOL 0x01
#define GPINTEN 0x02
#define DEFVAL 0x03
#define INTCON 0x04
#define IOCON 0x05
#define GPPU 0x06
#define INTF 0x07
#define INTCAP 0x08
#define GPIO 0x09
#define OLAT 0x0A
#define OPCODEW 0x40
#define OPCODER 0x41
// CS0 -> D2
const int slaveAPin = 2;
// CS1 -> D3
const int slaveBPin = 3;
// LED VAL
const uint8_t value = ~0x3F;
void setup() {
// put your setup code here, to run once:
// initialize SPI:
SPI.begin(); //Initialize the SPI_1 port.
SPI.setBitOrder(MSBFIRST); // Set the SPI_1 bit order
SPI.setDataMode(SPI_MODE0); //Set the SPI_1 data mode 0
SPI.setClockDivider(SPI_CLOCK_DIV64);
pinMode (slaveAPin, OUTPUT); // First chip for inputs
pinMode (slaveBPin, OUTPUT); // Second chip for outputs
digitalWrite (slaveAPin, HIGH);
digitalWrite (slaveBPin, HIGH);
}
void loop() {
// configuration led-io-expander
sendDataSPI(IOCON, 0x20);
// all pins = output
sendDataSPI(IODIR, 0x00);
// Enable LEDS
sendDataSPI(GPIO, value);
}
void sendDataSPI(uint8_t reg, uint8_t value){
digitalWrite (slaveBPin, LOW); // Take slave-select low
SPI.transfer(OPCODEW); // Send the MCP23S09 opcode, and write byte
SPI.transfer(reg); // Send the register we want to write
SPI.transfer(value); // Send the byte
digitalWrite (slaveBPin, HIGH); // Take slave-select high
}
STM32 HAL:
/**
******************************************************************************
* File Name : main.c
* Description : Main program body
******************************************************************************
** This notice applies to any and all portions of this file
* that are not between comment pairs USER CODE BEGIN and
* USER CODE END. Other portions of this file, whether
* inserted by the user or by software development tools
* are owned by their respective copyright owners.
*
* COPYRIGHT(c) 2017 STMicroelectronics
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* 3. Neither the name of STMicroelectronics nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
******************************************************************************
*/
/* Includes ------------------------------------------------------------------*/
#include "main.h"
#include "stm32f4xx_hal.h"
/* USER CODE BEGIN Includes */
/* USER CODE END Includes */
/* USER CODE BEGIN Defines */
#define IODIR 0x00
#define IPOL 0x01
#define GPINTEN 0x02
#define DEFVAL 0x03
#define INTCON 0x04
#define IOCON 0x05
#define GPPU 0x06
#define INTF 0x07
#define INTCAP 0x08
#define GPIO 0x09
#define OLAT 0x0A
#define OPCODEW 0x40
#define OPCODER 0x41
#define SPI_TRANSFER_TIMEOUT 1000
/* USER CODE END Defines */
/* Private variables ---------------------------------------------------------*/
SPI_HandleTypeDef hspi1;
UART_HandleTypeDef huart2;
/* USER CODE BEGIN PV */
/* Private variables ---------------------------------------------------------*/
/* USER CODE END PV */
/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
static void MX_GPIO_Init(void);
static void MX_SPI1_Init(void);
static void MX_USART2_UART_Init(void);
void sendDataSPI(uint8_t reg, uint8_t value);
int fgetc(FILE *f);
int fputc(int c, FILE *f);
/* USER CODE BEGIN PFP */
/* Private function prototypes -----------------------------------------------*/
/* USER CODE END PFP */
/* USER CODE BEGIN 0 */
/* USER CODE END 0 */
int main(void)
{
// LED VAL
uint8_t value = 0x3F;
/* USER CODE BEGIN 1 */
/* USER CODE END 1 */
/* MCU Configuration----------------------------------------------------------*/
/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
HAL_Init();
/* USER CODE BEGIN Init */
/* USER CODE END Init */
/* Configure the system clock */
SystemClock_Config();
/* USER CODE BEGIN SysInit */
/* USER CODE END SysInit */
/* Initialize all configured peripherals */
MX_GPIO_Init();
MX_SPI1_Init();
MX_USART2_UART_Init();
/* USER CODE BEGIN 2 */
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
// configuration led-io-expander
sendDataSPI(IOCON, 0x20);
// all pins = output
sendDataSPI(IODIR, 0x00);
// Enable LEDS
sendDataSPI(GPIO, value);
}
/* USER CODE END 3 */
}
// REGISTER, VALUE
void sendDataSPI(uint8_t reg, uint8_t value){
HAL_GPIO_WritePin(CS1_GPIO_Port, CS1_Pin, GPIO_PIN_RESET); // Take slave-select low
HAL_SPI_Transmit(&hspi1,(uint8_t *)OPCODEW,sizeof(uint8_t),SPI_TRANSFER_TIMEOUT); // Send the MCP23S09 opcode, and write bit
HAL_SPI_Transmit(&hspi1,(uint8_t *)®,sizeof(uint8_t),SPI_TRANSFER_TIMEOUT); // Send the register we want to write
HAL_SPI_Transmit(&hspi1,(uint8_t *)&value,sizeof(uint8_t),SPI_TRANSFER_TIMEOUT); // Send the byte
HAL_GPIO_WritePin(CS1_GPIO_Port, CS1_Pin, GPIO_PIN_SET); // Take slave-select high
}
int fputc(int c, FILE *f) {
return (HAL_UART_Transmit(&huart2, (uint8_t *)&c,1,HAL_MAX_DELAY));
}
int fgetc(FILE *f) {
char ch;
HAL_UART_Receive(&huart2,(uint8_t*)&ch,1,HAL_MAX_DELAY);
return (ch);
}
/** System Clock Configuration
*/
void SystemClock_Config(void)
{
RCC_OscInitTypeDef RCC_OscInitStruct;
RCC_ClkInitTypeDef RCC_ClkInitStruct;
/**Configure the main internal regulator output voltage
*/
__HAL_RCC_PWR_CLK_ENABLE();
__HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE1);
/**Initializes the CPU, AHB and APB busses clocks
*/
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI;
RCC_OscInitStruct.HSIState = RCC_HSI_ON;
RCC_OscInitStruct.HSICalibrationValue = 16;
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSI;
RCC_OscInitStruct.PLL.PLLM = 16;
RCC_OscInitStruct.PLL.PLLN = 336;
RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV4;
RCC_OscInitStruct.PLL.PLLQ = 4;
if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
{
_Error_Handler(__FILE__, __LINE__);
}
/**Initializes the CPU, AHB and APB busses clocks
*/
RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
|RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2;
RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;
if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2) != HAL_OK)
{
_Error_Handler(__FILE__, __LINE__);
}
/**Configure the Systick interrupt time
*/
HAL_SYSTICK_Config(HAL_RCC_GetHCLKFreq()/1000);
/**Configure the Systick
*/
HAL_SYSTICK_CLKSourceConfig(SYSTICK_CLKSOURCE_HCLK);
/* SysTick_IRQn interrupt configuration */
HAL_NVIC_SetPriority(SysTick_IRQn, 0, 0);
}
/* SPI1 init function */
static void MX_SPI1_Init(void)
{
/* SPI1 parameter configuration*/
hspi1.Instance = SPI1;
hspi1.Init.Mode = SPI_MODE_MASTER;
hspi1.Init.Direction = SPI_DIRECTION_2LINES;
hspi1.Init.DataSize = SPI_DATASIZE_8BIT;
hspi1.Init.CLKPolarity = SPI_POLARITY_LOW;
hspi1.Init.CLKPhase = SPI_PHASE_1EDGE;
hspi1.Init.NSS = SPI_NSS_SOFT;
hspi1.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_64;
hspi1.Init.FirstBit = SPI_FIRSTBIT_MSB;
hspi1.Init.TIMode = SPI_TIMODE_DISABLE;
hspi1.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE;
hspi1.Init.CRCPolynomial = 10;
if (HAL_SPI_Init(&hspi1) != HAL_OK)
{
_Error_Handler(__FILE__, __LINE__);
}
}
/* USART2 init function */
static void MX_USART2_UART_Init(void)
{
huart2.Instance = USART2;
huart2.Init.BaudRate = 115200;
huart2.Init.WordLength = UART_WORDLENGTH_8B;
huart2.Init.StopBits = UART_STOPBITS_1;
huart2.Init.Parity = UART_PARITY_NONE;
huart2.Init.Mode = UART_MODE_TX_RX;
huart2.Init.HwFlowCtl = UART_HWCONTROL_NONE;
huart2.Init.OverSampling = UART_OVERSAMPLING_16;
if (HAL_UART_Init(&huart2) != HAL_OK)
{
_Error_Handler(__FILE__, __LINE__);
}
}
/** Configure pins as
* Analog
* Input
* Output
* EVENT_OUT
* EXTI
*/
static void MX_GPIO_Init(void)
{
GPIO_InitTypeDef GPIO_InitStruct;
/* GPIO Ports Clock Enable */
__HAL_RCC_GPIOC_CLK_ENABLE();
__HAL_RCC_GPIOH_CLK_ENABLE();
__HAL_RCC_GPIOA_CLK_ENABLE();
__HAL_RCC_GPIOB_CLK_ENABLE();
/*Configure GPIO pin Output Level */
HAL_GPIO_WritePin(CS0_GPIO_Port, CS0_Pin, GPIO_PIN_RESET);
/*Configure GPIO pin Output Level */
HAL_GPIO_WritePin(CS1_GPIO_Port, CS1_Pin, GPIO_PIN_RESET);
/*Configure GPIO pin : B1_Pin */
GPIO_InitStruct.Pin = B1_Pin;
GPIO_InitStruct.Mode = GPIO_MODE_IT_FALLING;
GPIO_InitStruct.Pull = GPIO_NOPULL;
HAL_GPIO_Init(B1_GPIO_Port, &GPIO_InitStruct);
/*Configure GPIO pin : CS0_Pin */
GPIO_InitStruct.Pin = CS0_Pin;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
HAL_GPIO_Init(CS0_GPIO_Port, &GPIO_InitStruct);
/*Configure GPIO pin : CS1_Pin */
GPIO_InitStruct.Pin = CS1_Pin;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
HAL_GPIO_Init(CS1_GPIO_Port, &GPIO_InitStruct);
}
/* USER CODE END 4 */
You do not wait the SPI transfer to be completed before starting the next transmission in your sendDataSPI function. It should be modified like this:
void sendDataSPI(uint8_t reg, uint8_t value){
HAL_GPIO_WritePin(CS1_GPIO_Port, CS1_Pin, GPIO_PIN_RESET); // Take slave-select low
HAL_SPI_Transmit(&hspi1,(uint8_t *)OPCODEW,sizeof(uint8_t),SPI_TRANSFER_TIMEOUT); // Send the MCP23S09 opcode, and write bit
while(HAL_SPI_GetState(&hspi1) != HAL_SPI_STATE_READY);
HAL_SPI_Transmit(&hspi1,(uint8_t *)®,sizeof(uint8_t),SPI_TRANSFER_TIMEOUT); // Send the register we want to write
while(HAL_SPI_GetState(&hspi1) != HAL_SPI_STATE_READY);
HAL_SPI_Transmit(&hspi1,(uint8_t *)&value,sizeof(uint8_t),SPI_TRANSFER_TIMEOUT); // Send the byte
while(HAL_SPI_GetState(&hspi1) != HAL_SPI_STATE_READY);
HAL_GPIO_WritePin(CS1_GPIO_Port, CS1_Pin, GPIO_PIN_SET); // Take slave-select high
}
Also this line just sends rubbish and not 0x40.
HAL_SPI_Transmit(&hspi1,(uint8_t *)OPCODEW,sizeof(uint8_t),SPI_TRANSFER_TIMEOUT); // Send the MCP23S09 opcode, and write bit
Notice that your are casting OPCODEW to a uint8_t* so actually you will pass the 0x40 as a pointer (pointing to some random memory) and not as the data.
Has anybody used Duplex communication via VCP on Nucleo/or discovery single USART with RX TX interrupts.
Would appreciate sample code to Echo back(transmit) what is received .
There are certainly some example among the STM32CubeF4 and STM32CubeF1 package.
Also see this example, in which the microcontroller echos back the received bytes to the sender using UART RX interrupt:
#include "stm32f4xx.h"
UART_HandleTypeDef huart2;
/* Single byte to store input */
uint8_t byte;
void SystemClock_Config(void);
/* UART2 Interrupt Service Routine */
void USART2_IRQHandler(void)
{
HAL_UART_IRQHandler(&huart2);
}
/* This callback is called by the HAL_UART_IRQHandler when the given number of bytes are received */
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
if (huart->Instance == USART2)
{
/* Transmit one byte with 100 ms timeout */
HAL_UART_Transmit(&huart2, &byte, 1, 100);
/* Receive one byte in interrupt mode */
HAL_UART_Receive_IT(&huart2, &byte, 1);
}
}
void uart_gpio_init()
{
GPIO_InitTypeDef GPIO_InitStruct;
__GPIOA_CLK_ENABLE();
/**USART2 GPIO Configuration
PA2 ------> USART2_TX
PA3 ------> USART2_RX
*/
GPIO_InitStruct.Pin = GPIO_PIN_2 | GPIO_PIN_3;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Pull = GPIO_PULLUP;
GPIO_InitStruct.Speed = GPIO_SPEED_LOW;
GPIO_InitStruct.Alternate = GPIO_AF7_USART2;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
}
void uart_init()
{
__USART2_CLK_ENABLE();
huart2.Instance = USART2;
huart2.Init.BaudRate = 115200;
huart2.Init.WordLength = UART_WORDLENGTH_8B;
huart2.Init.StopBits = UART_STOPBITS_1;
huart2.Init.Parity = UART_PARITY_NONE;
huart2.Init.Mode = UART_MODE_TX_RX;
huart2.Init.HwFlowCtl = UART_HWCONTROL_NONE;
huart2.Init.OverSampling = UART_OVERSAMPLING_16;
HAL_UART_Init(&huart2);
/* Peripheral interrupt init*/
HAL_NVIC_SetPriority(USART2_IRQn, 0, 0);
HAL_NVIC_EnableIRQ(USART2_IRQn);
}
int main(void)
{
/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
HAL_Init();
uart_gpio_init();
uart_init();
HAL_UART_Receive_IT(&huart2, &byte, 1);
while(1)
{
}
}
Initialize the GPIO pins of the UART.
a. Enable the appropriate GPIO port's clock.
b. Configure the UART pins in alternate function mode.
Initialize the UART peripheral.
a. Enable the clock of the appropriate UART peripheral.
b. Configure BAUD rate, word length, stop and parity bits, flow control etc.
c. Enable the UART IRQ in NVIC and set the priority.
Call the HAL_UART_IRQHandler(UART_HandleTypeDef* huart); in the UART ISR (USART2_IRQHandler).
The HAL_UART_IRQHandler will call the HAL_UART_RxCpltCallback when the receive procedure is complete. In this callback you can transmit the received bytes.
Start the echo loop with a single HAL_UART_Receive_IT(&huart2, &byte, 1); call.
I'm trying to configure Pin PD3 as external interrupt, here is my configuration, but it seems its not working. So I defined everything and configured it, I still can't get the interrupt working in falling or rising edge.
void Configure_PD3(void) {
GPIO_InitTypeDef GPIO_InitStruct;
/* GPIO Ports Clock Enable */
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOD,ENABLE); // enables clock to portD
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_10MHz;
/*Configure GPIO pin : PD3 */
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_3;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IN;
GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_NOPULL;
GPIO_Init(GPIOD, &GPIO_InitStruct);
/* PD3 is connected to EXTI_Line3 */
EXTI_InitStruct.EXTI_Line = EXTI_Line3;
/* Enable interrupt */
EXTI_InitStruct.EXTI_LineCmd = ENABLE;
/* Interrupt mode */
EXTI_InitStruct.EXTI_Mode = EXTI_Mode_Interrupt;
/* Triggers on rising and falling edge */
EXTI_InitStruct.EXTI_Trigger = EXTI_Trigger_Rising_Falling;
/* Add to EXTI */
EXTI_Init(&EXTI_InitStruct);
/* EXTI interrupt init*/
NVIC_SetPriority(EXTI3_IRQn, 0);
NVIC_EnableIRQ(EXTI3_IRQn);
}
void EXTI3_IRQHandler(void)
{
int i = 0;
}
void main() {
// Don't put any variables on the stack here, as FreeRTOS seems to reuse this space.
// This might be valid as this thread will be dead after vTaskStartScheduler().
hardware_init();
Co
nfigure_PD3();
}
I have a simple project, created using CubeMX for the peripheral initialisation.
SPI is in slave mode, and appears to be initialised correctly, but when I clock 8 bits of data, the interrupt doesn't get called.
Here's the code
/* SPI1 init function */
static void MX_SPI1_Init(void)
{
hspi1.Instance = SPI1;
hspi1.Init.Mode = SPI_MODE_SLAVE;
hspi1.Init.Direction = SPI_DIRECTION_2LINES;
hspi1.Init.DataSize = SPI_DATASIZE_8BIT;
hspi1.Init.CLKPolarity = SPI_POLARITY_LOW;
hspi1.Init.CLKPhase = SPI_PHASE_1EDGE;
hspi1.Init.NSS = SPI_NSS_SOFT;
hspi1.Init.FirstBit = SPI_FIRSTBIT_MSB;
hspi1.Init.TIMode = SPI_TIMODE_DISABLE;
hspi1.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE;
hspi1.Init.CRCPolynomial = 7;
hspi1.Init.CRCLength = SPI_CRC_LENGTH_DATASIZE;
hspi1.Init.NSSPMode = SPI_NSS_PULSE_DISABLE;
if (HAL_SPI_Init(&hspi1) != HAL_OK)
{
Error_Handler();
}
}
void HAL_SPI_MspInit(SPI_HandleTypeDef* hspi)
{
GPIO_InitTypeDef GPIO_InitStruct;
if(hspi->Instance==SPI1)
{
/* USER CODE BEGIN SPI1_MspInit 0 */
/* USER CODE END SPI1_MspInit 0 */
/* Peripheral clock enable */
__HAL_RCC_SPI1_CLK_ENABLE();
/**SPI1 GPIO Configuration
PA5 ------> SPI1_SCK
PA6 ------> SPI1_MISO
PA7 ------> SPI1_MOSI
*/
GPIO_InitStruct.Pin = GPIO_PIN_5|GPIO_PIN_6|GPIO_PIN_7;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
GPIO_InitStruct.Alternate = GPIO_AF0_SPI1;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
/* Peripheral interrupt init */
HAL_NVIC_SetPriority(SPI1_IRQn, 0, 0);
HAL_NVIC_EnableIRQ(SPI1_IRQn);
/* USER CODE BEGIN SPI1_MspInit 1 */
/* USER CODE END SPI1_MspInit 1 */
}
}
void SPI1_IRQHandler(void)
{
/* USER CODE BEGIN SPI1_IRQn 0 */
/* USER CODE END SPI1_IRQn 0 */
HAL_SPI_IRQHandler(&hspi1);
/* USER CODE BEGIN SPI1_IRQn 1 */
spi_interrupt();
/* USER CODE END SPI1_IRQn 1 */
}
The spi_interrupt() is my specific code for the interrupt actions, and a breakpoint in there never fires.
I've got a scope on the CLKIN pin, and its definitely got the 8 clocks.
ST's HAL library won't enable the actual peripheral interrupts in the initialization function.
For almost all of the peripherals an additional function has to be called which always has the following name structure HAL_<peripheral>_<action>_IT so in case of SPI RX it is called HAL_SPI_Receive_IT.
This enables actually the SPI RX interrupt by setting the correct bit with a macro called: __HAL_SPI_ENABLE_IT(hspi, (SPI_IT_RXNE | SPI_IT_ERR)).
Note that if the number of expected bytes (this value is passed in the HAL_SPI_Receive_IT by the user) is reached then the HAL_SPI_IRQHandler will disable the SPI RX interrupt again, thus a repeated HAL_SPI_Receive_IT call is needed in case of a new reception.
I've successfully established CAN communication with CAN1 of two STM32F105vC(s)(Which has two CANs), and I can send and receive CAN frames.
But when I change my code to use CAN2, it works, I mean it acknowledges the received CAN frames but the receive interrupt is not being called at all.
I thought it's filter configuration, I changed the BankNumber multipletimes (tried 0, 1, 14, 20), no luck.
Here is my configurations:
Initilizing:
init_HAL_CAN(CAN2);
if(HAL_CAN_Receive_IT(canHandle, CAN_FIFO0) != HAL_OK)
{
/* Reception Error */
Error_Handler();
}
Receive callback:
void HAL_CAN_RxCpltCallback(CAN_HandleTypeDef* CanHandle)
{
LED_DBG_TOGGLE();
// other
// stuff
// here
//...
/* Receive */
if(HAL_CAN_Receive_IT(CanHandle, CAN_FIFO0) != HAL_OK)
{
/* Reception Error */
Error_Handler();
}
}
Init function:
void init_HAL_CAN(CAN_TypeDef* _CANInstance)
{
CAN_FilterConfTypeDef sFilterConfig;
static CanTxMsgTypeDef TxMessage;
static CanRxMsgTypeDef RxMessage;
/*##-1- Configure the CAN peripheral #######################################*/
canHandle->Instance = _CANInstance;
canHandle->pTxMsg = &TxMessage;
canHandle->pRxMsg = &RxMessage;
canHandle->Init.TTCM = DISABLE; //Non time trigger communication mode //
canHandle->Init.ABOM = DISABLE; //The software automatically Bus-off management //
canHandle->Init.AWUM = DISABLE; //Sleep mode wake by software (clear CAN-> MCR SLEEP) (automatic wake-up mode)//
canHandle->Init.NART = DISABLE; //Disable automatic transfer message (non-automatic retransmission mode)//
canHandle->Init.RFLM = DISABLE; //The message is not locked, the new cover the old //
canHandle->Init.TXFP = DISABLE; // Priority is determined by the message identifier //
canHandle->Init.Mode = CAN_MODE_NORMAL;
canHandle->Init.SJW = CAN_SJW_1TQ;
canHandle->Init.BS1 = CAN_BS1_2TQ;
canHandle->Init.BS2 = CAN_BS2_1TQ;
canHandle->Init.Prescaler = 7;
if(HAL_CAN_Init(canHandle) != HAL_OK)
{
/* Initialization Error */
Error_Handler();
return false;
}
/*##-2- Configure the CAN Filter ###########################################*/
CAN_FilterConfTypeDef sFilterConfig;
sFilterConfig.FilterNumber = 0;
sFilterConfig.FilterMode = CAN_FILTERMODE_IDMASK;
sFilterConfig.FilterScale = CAN_FILTERSCALE_32BIT;
sFilterConfig.FilterIdHigh = 0x0000;
sFilterConfig.FilterIdLow = 0x0000;
sFilterConfig.FilterMaskIdHigh = 0x0000;
sFilterConfig.FilterMaskIdLow = 0x0000;
sFilterConfig.FilterFIFOAssignment = 0;
sFilterConfig.FilterActivation = ENABLE;
sFilterConfig.BankNumber = 20;
if (HAL_CAN_ConfigFilter(canHandle, &sFilterConfig) != HAL_OK)
{
/* Filter configuration Error */
Error_Handler();
return false;
}
}
IRQHandler (placed in stm32f1xx_it.c):
void CAN2_RX0_IRQHandler(void)
{
HAL_CAN_IRQHandler(canHandle);
}
MspInit function (placed in stm32f1xx_hal_msp.c) :
void HAL_CAN_MspInit(CAN_HandleTypeDef *hcan)
{
GPIO_InitTypeDef GPIO_InitStruct;
/*##-1- Enable peripherals and GPIO Clocks #################################*/
/* CAN1 Periph clock enable */
CANx1_CLK_ENABLE(); // Need to enable CAN1 clock too.
CANx2_CLK_ENABLE();
/* Enable GPIO clock ****************************************/
CANx2_GPIO_CLK_ENABLE();
/* CAN2 needs no remapping *******/
/*##-2- Configure peripheral GPIO ##########################################*/
/* CAN1 TX GPIO pin configuration */
GPIO_InitStruct.Pin = CANx2_TX_PIN;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
GPIO_InitStruct.Pull = GPIO_PULLUP;
HAL_GPIO_Init(CANx2_TX_GPIO_PORT, &GPIO_InitStruct);
/* CAN1 RX GPIO pin configuration */
GPIO_InitStruct.Pin = CANx2_RX_PIN;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
GPIO_InitStruct.Pull = GPIO_PULLUP;
HAL_GPIO_Init(CANx2_RX_GPIO_PORT, &GPIO_InitStruct);
/*##-3- Configure the NVIC #################################################*/
/* NVIC configuration for CAN2 Reception complete interrupt */
HAL_NVIC_SetPriority(CAN2_RX0_IRQn, 1, 0);
HAL_NVIC_EnableIRQ(CAN2_RX0_IRQn);
}
I should mention again, that following codes works perfectly with CAN1 (of course with changing CAN2 to CAN1, etc).
And it's clear that the CAN controller is working because the other board that is transmitting CAN frames, receives the acknowledges (and does not get any transmitting error or timeout), the only problem here is Receive Interrupts, why?
I had similar issue and what help was reference manual:
CAN2SB[5:0]: CAN2 start bank
These bits are set and cleared by software. They define the start bank for the CAN2
interface (Slave) in the range 0 to 27.
Note: When CAN2SB[5:0] = 28d, all the filters to CAN1 can be used.
When CAN2SB[5:0] is set to 0, no filters are assigned to CAN1
sFilterConfig.BankNumber = 20;
Please change
sFilterConfig.BankNumber = 20;
to
sFilterConfig.BankNumber = 0x2d;
You must perform settings of all filters through hcan1. Even for Can2.
In connectivity line
devices, the registers from offset 0x200 to 31C are present only in CAN1.
...and on offset 0x200 begins filter settings...