Я разрабатываю устройство принимающие пакеты по CAN-шине и записывающая их на SD-карту. Использую плату Iskra JS с микроконтроллером STM32F405RGT6 и CAN-BUS Shield. Я написал программу способную принимать пакеты, но не могу записывать данные. Взаимодействие между микроконтроллером и SD-картой, проходит через SPI, CS - PC3 я назвал этот пин CS_TF настроен он на выход. Помоги дополнить код: инициализация SD , функция чтения и записи.
ЭТО main.c
/* USER CODE BEGIN Header */
/**
******************************************************************************
* @file : main.c
* @brief : Main program body
******************************************************************************
* @attention
*
* Copyright © 2024 STMicroelectronics.
* All rights reserved.
*
* This software is licensed under terms that can be found in the LICENSE file
* in the root directory of this software component.
* If no LICENSE file comes with this software, it is provided AS-IS.
*
******************************************************************************
* @author
*
* With version made by Kim Maksim 2024.
* email: <PRESIDIO_ANONYMIZED_EMAIL_ADDRESS>
*
*********************************
/
/ USER CODE END Header /
/ Includes ------------------------------------------------------------------/
#include “main.h”
#include “fatfs.h”

/ Private includes ----------------------------------------------------------/
/ USER CODE BEGIN Includes /
#include “CANSPI.h”
#include “string.h”
#include “stdio.h”

/ USER CODE END Includes /

/ Private typedef -----------------------------------------------------------/
/ USER CODE BEGIN PTD /

/ USER CODE END PTD /

/ Private define ------------------------------------------------------------/
/ USER CODE BEGIN PD /

/ USER CODE END PD /

/ Private macro -------------------------------------------------------------/
/ USER CODE BEGIN PM /

/ USER CODE END PM /

/ Private variables ---------------------------------------------------------/
RTC_HandleTypeDef hrtc;

SPI_HandleTypeDef hspi2;

UART_HandleTypeDef huart4;

/ USER CODE BEGIN PV /
char trans_str[256] = {“Default”};
char ascii_str[9] = {“Default”};
char dateTime[50];
uint8_t Rx = 100;
uCAN_MSG rxMessage;

/ USER CODE END PV /

/ Private function prototypes -----------------------------------------------/
void SystemClock_Config(void);
static void MX_GPIO_Init(void);
static void MX_SPI2_Init(void);
static void MX_UART4_Init(void);
static void MX_RTC_Init(void);
/ USER CODE BEGIN PFP /
void GetDateTime(char buffer);
void TransformToASCII(char buffer, uint8_t data);
void print(char message[256])
{
HAL_UART_Transmit(&huart4, (uint8_t )message, strlen(message), 100);
}

/ USER CODE END PFP /

/ Private user code ---------------------------------------------------------/
/ USER CODE BEGIN 0 /

/ USER CODE END 0 */

/
* @brief The application entry point.
* @retval int
/
int main(void)
{
/ 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_SPI2_Init();
MX_UART4_Init();
MX_RTC_Init();
MX_FATFS_Init();
/ USER CODE BEGIN 2 /
if (!CANSPI_Initialize())
{
print(“Error Initialize CANSPI\n”);
while(1) { }
}

/ USER CODE END 2 /

/ Infinite loop /
/ USER CODE BEGIN WHILE /
print(“\nNow begin…\n”);
HAL_Delay(1000);

while (1)
{
/ USER CODE END WHILE /

/ USER CODE BEGIN 3 */
if (CANSPI_isBussOff())
{
print(“IS BUSS OFF\n”);
HAL_Delay(1000);
}
if (CANSPI_isRxErrorPassive()) {
print(“Rx Error Passive\n”);
HAL_Delay(1000);
}
if (CANSPI_isTxErrorPassive()) {
print(“Tx Error Passive\n”);
HAL_Delay(1000);
}

GetDateTime(dateTime);

if(CANSPI_ReceiveTest(&rxMessage))
{
TransformToASCII(ascii_str, rxMessage.array);
snprintf(trans_str, 128, “%s ID: %04lX \tDATA: %02X %02X %02X %02X %02X %02X %02X %02X \tASCII: %s\n”,
dateTime,
rxMessage.frame.id,
rxMessage.frame.data0,
rxMessage.frame.data1,
rxMessage.frame.data2,
rxMessage.frame.data3,
rxMessage.frame.data4,
rxMessage.frame.data5,
rxMessage.frame.data6,
rxMessage.frame.data7,
ascii_str);
}
print(trans_str);
HAL_GPIO_TogglePin(LED1_GPIO_Port, LED1_Pin);
}
HAL_UART_Transmit(&huart4, (uint8_t )“What?!”, 10, 100);

/ USER CODE END 3 */
}

/
* @brief System Clock Configuration
* @retval None
/
void SystemClock_Config(void)
{
RCC_OscInitTypeDef RCC_OscInitStruct = {0};
RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};

/ Configure the main internal regulator output voltage
/
__HAL_RCC_PWR_CLK_ENABLE();
__HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE1);

/ Initializes the RCC Oscillators according to the specified parameters
* in the RCC_OscInitTypeDef structure.
/
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI|RCC_OSCILLATORTYPE_LSI;
RCC_OscInitStruct.HSIState = RCC_HSI_ON;
RCC_OscInitStruct.HSICalibrationValue = RCC_HSICALIBRATION_DEFAULT;
RCC_OscInitStruct.LSIState = RCC_LSI_ON;
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSI;
RCC_OscInitStruct.PLL.PLLM = 8;
RCC_OscInitStruct.PLL.PLLN = 72;
RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV2;
RCC_OscInitStruct.PLL.PLLQ = 4;
if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
{
Error_Handler();
}

/ Initializes the CPU, AHB and APB buses 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();
}
}

/
* @brief RTC Initialization Function
* @param None
* @retval None
/
static void MX_RTC_Init(void)
{

/ USER CODE BEGIN RTC_Init 0 /
RTC_TimeTypeDef sTime;
/ USER CODE END RTC_Init 0 /

/ USER CODE BEGIN RTC_Init 1 /
sTime.Hours = -17; // 0 hour, I don`t know why.
sTime.Minutes = 0;
sTime.Seconds = 0;
/ USER CODE END RTC_Init 1 /

/ Initialize RTC Only
/
hrtc.Instance = RTC;
hrtc.Init.HourFormat = RTC_HOURFORMAT_24;
hrtc.Init.AsynchPrediv = 127;
hrtc.Init.SynchPrediv = 255;
hrtc.Init.OutPut = RTC_OUTPUT_DISABLE;
hrtc.Init.OutPutPolarity = RTC_OUTPUT_POLARITY_HIGH;
hrtc.Init.OutPutType = RTC_OUTPUT_TYPE_OPENDRAIN;
if (HAL_RTC_Init(&hrtc) != HAL_OK)
{
Error_Handler();
}
/ USER CODE BEGIN RTC_Init 2 /
HAL_RTC_SetTime(&hrtc, &sTime, RTC_FORMAT_BIN); // Установка времени

/ USER CODE END RTC_Init 2 */

}

/
* @brief SPI2 Initialization Function
* @param None
* @retval None
/
static void MX_SPI2_Init(void)
{

/ USER CODE BEGIN SPI2_Init 0 /

/ USER CODE END SPI2_Init 0 /

/ USER CODE BEGIN SPI2_Init 1 /

/ USER CODE END SPI2_Init 1 /
/ SPI2 parameter configuration/
hspi2.Instance = SPI2;
hspi2.Init.Mode = SPI_MODE_MASTER;
hspi2.Init.Direction = SPI_DIRECTION_2LINES;
hspi2.Init.DataSize = SPI_DATASIZE_8BIT;
hspi2.Init.CLKPolarity = SPI_POLARITY_LOW;
hspi2.Init.CLKPhase = SPI_PHASE_1EDGE;
hspi2.Init.NSS = SPI_NSS_SOFT;
hspi2.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_4;
hspi2.Init.FirstBit = SPI_FIRSTBIT_MSB;
hspi2.Init.TIMode = SPI_TIMODE_DISABLE;
hspi2.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE;
hspi2.Init.CRCPolynomial = 10;
if (HAL_SPI_Init(&hspi2) != HAL_OK)
{
Error_Handler();
}
/ USER CODE BEGIN SPI2_Init 2 /

/ USER CODE END SPI2_Init 2 */

}

/
* @brief UART4 Initialization Function
* @param None
* @retval None
/
static void MX_UART4_Init(void)
{

/ USER CODE BEGIN UART4_Init 0 /

/ USER CODE END UART4_Init 0 /

/ USER CODE BEGIN UART4_Init 1 /

/ USER CODE END UART4_Init 1 /
huart4.Instance = UART4;
huart4.Init.BaudRate = 115200;
huart4.Init.WordLength = UART_WORDLENGTH_8B;
huart4.Init.StopBits = UART_STOPBITS_1;
huart4.Init.Parity = UART_PARITY_NONE;
huart4.Init.Mode = UART_MODE_TX_RX;
huart4.Init.HwFlowCtl = UART_HWCONTROL_NONE;
huart4.Init.OverSampling = UART_OVERSAMPLING_16;
if (HAL_UART_Init(&huart4) != HAL_OK)
{
Error_Handler();
}
/ USER CODE BEGIN UART4_Init 2 /

/ USER CODE END UART4_Init 2 */

}

/
* @brief GPIO Initialization Function
* @param None
* @retval None
/
static void MX_GPIO_Init(void)
{
GPIO_InitTypeDef GPIO_InitStruct = {0};
/ USER CODE BEGIN MX_GPIO_Init_1 /
/ USER CODE END MX_GPIO_Init_1 /

/ GPIO Ports Clock Enable */
__HAL_RCC_GPIOH_CLK_ENABLE();
__HAL_RCC_GPIOC_CLK_ENABLE();
__HAL_RCC_GPIOA_CLK_ENABLE();
__HAL_RCC_GPIOB_CLK_ENABLE();

/*Configure GPIO pin Output Level */
HAL_GPIO_WritePin(GPIOC, CS_TF_Pin|CAN_CS_Pin, GPIO_PIN_SET);

/*Configure GPIO pin Output Level */
HAL_GPIO_WritePin(LED1_GPIO_Port, LED1_Pin, GPIO_PIN_RESET);

/*Configure GPIO pins : CS_TF_Pin CAN_CS_Pin */
GPIO_InitStruct.Pin = CS_TF_Pin|CAN_CS_Pin;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_MEDIUM;
HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);

/*Configure GPIO pin : LED1_Pin /
GPIO_InitStruct.Pin = LED1_Pin;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
HAL_GPIO_Init(LED1_GPIO_Port, &GPIO_InitStruct);

/ USER CODE BEGIN MX_GPIO_Init_2 /
/ USER CODE END MX_GPIO_Init_2 /
}

/ USER CODE BEGIN 4 */

/
* @brief This function return Date and Time
* @retval None
* /
void GetDateTime(char buffer)
{
RTC_TimeTypeDef sTime;

// Получаем текущую дату и время
HAL_RTC_GetTime(&hrtc, &sTime, RTC_FORMAT_BIN);

// Форматируем строку времени
snprintf(buffer, 50, “[%02d:%02d:%02d]”,
sTime.Hours,
sTime.Minutes,
sTime.Seconds);
}

void TransformToASCII(char buffer, uint8_t data)
{
for (int i = 6; i < 14; i++)
{
uint8_t data_byte = ((uint8_t )data)[i];
if (data_byte >= 32 && data_byte <= 126) {
buffer[i-6] = (char)data_byte;
} else {
buffer[i-6] = ‘.’;
}
}
buffer[8] = ‘\0’;
}

/ USER CODE END 4 */

/
* @brief This function is executed in case of error occurrence.
* @retval None
/
void Error_Handler(void)
{
/ USER CODE BEGIN Error_Handler_Debug /
/ User can add his own implementation to report the HAL error return state /
__disable_irq();
while (1)
{
}
/ USER CODE END Error_Handler_Debug */
}

#ifdef USE_FULL_ASSERT
/
* @brief Reports the name of the source file and the source line number
* where the assert_param error has occurred.
* @param file: pointer to the source file name
* @param line: assert_param error line source number
* @retval None
/
void assert_failed(uint8_t file, uint32_t line)
{
/ USER CODE BEGIN 6 /
/ 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) /
/ USER CODE END 6 /
}
#endif / USE_FULL_ASSERT /


ЭТО mcp2515.c
/
© 2016 Microchip Technology Inc. and its subsidiaries. You may use this
software and any derivatives exclusively with Microchip products.

THIS SOFTWARE IS SUPPLIED BY MICROCHIP “AS IS”. NO WARRANTIES, WHETHER
EXPRESS, IMPLIED OR STATUTORY, APPLY TO THIS SOFTWARE, INCLUDING ANY IMPLIED
WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY, AND FITNESS FOR A
PARTICULAR PURPOSE, OR ITS INTERACTION WITH MICROCHIP PRODUCTS, COMBINATION
WITH ANY OTHER PRODUCTS, OR USE IN ANY APPLICATION.

IN NO EVENT WILL MICROCHIP BE LIABLE FOR ANY INDIRECT, SPECIAL, PUNITIVE,
INCIDENTAL OR CONSEQUENTIAL LOSS, DAMAGE, COST OR EXPENSE OF ANY KIND
WHATSOEVER RELATED TO THE SOFTWARE, HOWEVER CAUSED, EVEN IF MICROCHIP HAS
BEEN ADVISED OF THE POSSIBILITY OR THE DAMAGES ARE FORESEEABLE. TO THE
FULLEST EXTENT ALLOWED BY LAW, MICROCHIP’S TOTAL LIABILITY ON ALL CLAIMS IN
ANY WAY RELATED TO THIS SOFTWARE WILL NOT EXCEED THE AMOUNT OF FEES, IF ANY,
THAT YOU HAVE PAID DIRECTLY TO MICROCHIP FOR THIS SOFTWARE.

MICROCHIP PROVIDES THIS SOFTWARE CONDITIONALLY UPON YOUR ACCEPTANCE OF THESE
TERMS.
/

#include “mcp2515.h”
#include “main.h”
#include “string.h”
#include “stdio.h”

/ SPI related variables /
extern SPI_HandleTypeDef hspi2;
#define SPI_CAN &hspi2
#define SPI_TIMEOUT 10
#define MCP2515_CS_HIGH() HAL_GPIO_WritePin(CAN_CS_GPIO_Port, CAN_CS_Pin, GPIO_PIN_SET)
#define MCP2515_CS_LOW() HAL_GPIO_WritePin(CAN_CS_GPIO_Port, CAN_CS_Pin, GPIO_PIN_RESET)

#define SD_CS_HIGH() HAL_GPIO_WritePin(CS_TF_GPIO_Port, CS_TF_Pin, GPIO_PIN_SET)
#define SD_CS_LOW() HAL_GPIO_WritePin(CS_TF_GPIO_Port, CS_TF_Pin, GPIO_PIN_RESET)

/ Prototypes */
static void SPI_Tx(uint8_t data);
static void SPI_TxBuffer(uint8_t *buffer, uint8_t length);
static uint8_t SPI_Rx(void);
static void SPI_RxBuffer(uint8_t buffer, uint8_t length);

/ initialize MCP2515 /
bool MCP2515_Initialize(void)
{
MCP2515_CS_HIGH();

uint8_t loop = 10;

do {
/ check SPI Ready /
if(HAL_SPI_GetState(SPI_CAN) == HAL_SPI_STATE_READY)
return true;

loop–;
} while(loop > 0);

return false;
}

/ change mode as configuration mode /
bool MCP2515_SetConfigMode(void)
{
/ configure CANCTRL Register /
MCP2515_WriteByte(MCP2515_CANCTRL, 0x80);

uint8_t loop = 10;

do {
/ confirm mode configuration /
if((MCP2515_ReadByte(MCP2515_CANSTAT) & 0xE0) == 0x80)
return true;

loop–;
} while(loop > 0);

return false;
}

/ change mode as normal mode /
bool MCP2515_SetNormalMode(void)
{
/ configure CANCTRL Register /
MCP2515_WriteByte(MCP2515_CANCTRL, 0x00);

uint8_t loop = 10;

do {
/ confirm mode configuration /
if((MCP2515_ReadByte(MCP2515_CANSTAT) & 0xE0) == 0x00)
return true;

loop–;
} while(loop > 0);

return false;
}

/ Entering sleep mode /
bool MCP2515_SetSleepMode(void)
{
/ configure CANCTRL Register /
MCP2515_WriteByte(MCP2515_CANCTRL, 0x20);

uint8_t loop = 10;

do {
/ confirm mode configuration /
if((MCP2515_ReadByte(MCP2515_CANSTAT) & 0xE0) == 0x20)
return true;

loop–;
} while(loop > 0);

return false;
}

/ MCP2515 SPI-Reset /
void MCP2515_Reset(void)
{
MCP2515_CS_LOW();

SPI_Tx(MCP2515_RESET);

MCP2515_CS_HIGH();
}

/ read single byte /
uint8_t MCP2515_ReadByte (uint8_t address)
{
uint8_t retVal;

MCP2515_CS_LOW();

SPI_Tx(MCP2515_READ);
SPI_Tx(address);
retVal = SPI_Rx();

MCP2515_CS_HIGH();

return retVal;
}

/ read buffer */
void MCP2515_ReadRxSequence(uint8_t instruction, uint8_t data, uint8_t length)
{
MCP2515_CS_LOW();

SPI_Tx(instruction);
SPI_RxBuffer(data, length);

MCP2515_CS_HIGH();
}

/ write single byte /
void MCP2515_WriteByte(uint8_t address, uint8_t data)
{
MCP2515_CS_LOW();

SPI_Tx(MCP2515_WRITE);
SPI_Tx(address);
SPI_Tx(data);

MCP2515_CS_HIGH();
}

/ write buffer */
void MCP2515_WriteByteSequence(uint8_t startAddress, uint8_t endAddress, uint8_t data)
{
MCP2515_CS_LOW();

SPI_Tx(MCP2515_WRITE);
SPI_Tx(startAddress);
SPI_TxBuffer(data, (endAddress - startAddress + 1));

MCP2515_CS_HIGH();
}

/ write to TxBuffer /
void MCP2515_LoadTxSequence(uint8_t instruction, uint8_t idReg, uint8_t dlc, uint8_t data)
{
MCP2515_CS_LOW();

SPI_Tx(instruction);
SPI_TxBuffer(idReg, 4);
SPI_Tx(dlc);
SPI_TxBuffer(data, dlc);

MCP2515_CS_HIGH();
}

/ write to TxBuffer(1 byte) /
void MCP2515_LoadTxBuffer(uint8_t instruction, uint8_t data)
{
MCP2515_CS_LOW();

SPI_Tx(instruction);
SPI_Tx(data);

MCP2515_CS_HIGH();
}

/ request to send /
void MCP2515_RequestToSend(uint8_t instruction)
{
MCP2515_CS_LOW();

SPI_Tx(instruction);

MCP2515_CS_HIGH();
}

/ read status /
uint8_t MCP2515_ReadStatus(void)
{
uint8_t retVal;

MCP2515_CS_LOW();

SPI_Tx(MCP2515_READ_STATUS);
retVal = SPI_Rx();

MCP2515_CS_HIGH();

return retVal;
}

/ read RX STATUS register /
uint8_t MCP2515_GetRxStatus(void)
{
uint8_t retVal;

MCP2515_CS_LOW();

SPI_Tx(MCP2515_RX_STATUS);
retVal = SPI_Rx();

MCP2515_CS_HIGH();

return retVal;
}

/ Use when changing register value /
void MCP2515_BitModify(uint8_t address, uint8_t mask, uint8_t data)
{
MCP2515_CS_LOW();

SPI_Tx(MCP2515_BIT_MOD);
SPI_Tx(address);
SPI_Tx(mask);
SPI_Tx(data);

MCP2515_CS_HIGH();
}

/ SPI Tx wrapper function /
static void SPI_Tx(uint8_t data)
{
HAL_SPI_Transmit(SPI_CAN, &data, 1, SPI_TIMEOUT);
}

/ SPI Tx wrapper function /
static void SPI_TxBuffer(uint8_t buffer, uint8_t length)
{
HAL_SPI_Transmit(SPI_CAN, buffer, length, SPI_TIMEOUT);
}

/ SPI Rx wrapper function /
static uint8_t SPI_Rx(void)
{
uint8_t retVal;
HAL_SPI_Receive(SPI_CAN, &retVal, 1, SPI_TIMEOUT);
return retVal;
}

/ SPI Rx wrapper function /
static void SPI_RxBuffer(uint8_t buffer, uint8_t length)
{
HAL_SPI_Receive(SPI_CAN, buffer, length, SPI_TIMEOUT);
}


ЭТО CANSPI.c
/
© 2016 Microchip Technology Inc. and its subsidiaries. You may use this
software and any derivatives exclusively with Microchip products.

THIS SOFTWARE IS SUPPLIED BY MICROCHIP “AS IS”. NO WARRANTIES, WHETHER
EXPRESS, IMPLIED OR STATUTORY, APPLY TO THIS SOFTWARE, INCLUDING ANY IMPLIED
WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY, AND FITNESS FOR A
PARTICULAR PURPOSE, OR ITS INTERACTION WITH MICROCHIP PRODUCTS, COMBINATION
WITH ANY OTHER PRODUCTS, OR USE IN ANY APPLICATION.

IN NO EVENT WILL MICROCHIP BE LIABLE FOR ANY INDIRECT, SPECIAL, PUNITIVE,
INCIDENTAL OR CONSEQUENTIAL LOSS, DAMAGE, COST OR EXPENSE OF ANY KIND
WHATSOEVER RELATED TO THE SOFTWARE, HOWEVER CAUSED, EVEN IF MICROCHIP HAS
BEEN ADVISED OF THE POSSIBILITY OR THE DAMAGES ARE FORESEEABLE. TO THE
FULLEST EXTENT ALLOWED BY LAW, MICROCHIP’S TOTAL LIABILITY ON ALL CLAIMS IN
ANY WAY RELATED TO THIS SOFTWARE WILL NOT EXCEED THE AMOUNT OF FEES, IF ANY,
THAT YOU HAVE PAID DIRECTLY TO MICROCHIP FOR THIS SOFTWARE.

MICROCHIP PROVIDES THIS SOFTWARE CONDITIONALLY UPON YOUR ACCEPTANCE OF THESE
TERMS.
/

#include “CANSPI.h”
#include “mcp2515.h”
#include “main.h”
#include “string.h”
#include “stdio.h”

/ Local Function Prototypes /
static uint32_t convertReg2ExtendedCANid(uint8_t tempRXBn_EIDH, uint8_t tempRXBn_EIDL, uint8_t tempRXBn_SIDH, uint8_t tempRXBn_SIDL);
static uint32_t convertReg2StandardCANid(uint8_t tempRXBn_SIDH, uint8_t tempRXBn_SIDL) ;
static void convertCANid2Reg(uint32_t tempPassedInID, uint8_t canIdType, id_reg_t passedIdReg);

/ Local Variables /
ctrl_status_t ctrlStatus;
ctrl_error_status_t errorStatus;
id_reg_t idReg;

/ CAN SPI APIs /

/ Entering Sleep Mode /
void CANSPI_Sleep(void)
{
/ Clear CAN bus wakeup interrupt /
MCP2515_BitModify(MCP2515_CANINTF, 0x40, 0x00);

/ Enable CAN bus activity wakeup /
MCP2515_BitModify(MCP2515_CANINTE, 0x40, 0x40);

MCP2515_SetSleepMode();
}

/ Initialize CAN /
bool CANSPI_Initialize(void)
{
RXF0 RXF0reg;
RXF1 RXF1reg;
RXF2 RXF2reg;
RXF3 RXF3reg;
RXF4 RXF4reg;
RXF5 RXF5reg;
RXM0 RXM0reg;
RXM1 RXM1reg;

/ Intialize Rx Mask values /
RXM0reg.RXM0SIDH = 0x00;
RXM0reg.RXM0SIDL = 0x00;
RXM0reg.RXM0EID8 = 0x00;
RXM0reg.RXM0EID0 = 0x00;

RXM1reg.RXM1SIDH = 0x00;
RXM1reg.RXM1SIDL = 0x00;
RXM1reg.RXM1EID8 = 0x00;
RXM1reg.RXM1EID0 = 0x00;

/ Intialize Rx Filter values /
RXF0reg.RXF0SIDH = 0x00;
RXF0reg.RXF0SIDL = 0x00; //Starndard Filter
RXF0reg.RXF0EID8 = 0x00;
RXF0reg.RXF0EID0 = 0x00;

RXF1reg.RXF1SIDH = 0x00;
RXF1reg.RXF1SIDL = 0x08; //Exntended Filter
RXF1reg.RXF1EID8 = 0x00;
RXF1reg.RXF1EID0 = 0x00;

RXF2reg.RXF2SIDH = 0x00;
RXF2reg.RXF2SIDL = 0x00;
RXF2reg.RXF2EID8 = 0x00;
RXF2reg.RXF2EID0 = 0x00;

RXF3reg.RXF3SIDH = 0x00;
RXF3reg.RXF3SIDL = 0x00;
RXF3reg.RXF3EID8 = 0x00;
RXF3reg.RXF3EID0 = 0x00;

RXF4reg.RXF4SIDH = 0x00;
RXF4reg.RXF4SIDL = 0x00;
RXF4reg.RXF4EID8 = 0x00;
RXF4reg.RXF4EID0 = 0x00;

RXF5reg.RXF5SIDH = 0x00;
RXF5reg.RXF5SIDL = 0x08;
RXF5reg.RXF5EID8 = 0x00;
RXF5reg.RXF5EID0 = 0x00;

/ Intialize MCP2515, check SPI /
if(!MCP2515_Initialize())
{
print(“Error Initialize MCP2515\n”);
return false;
}
MCP2515_Reset();

if (!MCP2515_SetConfigMode())
{
print(“Error Transmit to Configuration Mode\n”);
return false;
}

/ Configure filter & mask /
MCP2515_WriteByteSequence(MCP2515_RXM0SIDH, MCP2515_RXM0EID0, &(RXM0reg.RXM0SIDH));
MCP2515_WriteByteSequence(MCP2515_RXM1SIDH, MCP2515_RXM1EID0, &(RXM1reg.RXM1SIDH));
MCP2515_WriteByteSequence(MCP2515_RXF0SIDH, MCP2515_RXF0EID0, &(RXF0reg.RXF0SIDH));
MCP2515_WriteByteSequence(MCP2515_RXF1SIDH, MCP2515_RXF1EID0, &(RXF1reg.RXF1SIDH));
MCP2515_WriteByteSequence(MCP2515_RXF2SIDH, MCP2515_RXF2EID0, &(RXF2reg.RXF2SIDH));
MCP2515_WriteByteSequence(MCP2515_RXF3SIDH, MCP2515_RXF3EID0, &(RXF3reg.RXF3SIDH));
MCP2515_WriteByteSequence(MCP2515_RXF4SIDH, MCP2515_RXF4EID0, &(RXF4reg.RXF4SIDH));
MCP2515_WriteByteSequence(MCP2515_RXF5SIDH, MCP2515_RXF5EID0, &(RXF5reg.RXF5SIDH));

/ Accept All (Standard + Extended) /
MCP2515_WriteByte(MCP2515_RXB0CTRL, 0x04); //Enable BUKT, Accept Filter 0
MCP2515_WriteByte(MCP2515_RXB1CTRL, 0x01); //Accept Filter 1

/ Для 500 Кб/с 0x41 0xB6 0x00 /
MCP2515_WriteByte(MCP2515_CNF1, 0x41);
MCP2515_WriteByte(MCP2515_CNF2, 0xB6);
MCP2515_WriteByte(MCP2515_CNF3, 0x00);

/ Normal /
if(!MCP2515_SetNormalMode())
{
print(“Error Set Normal Mode MCP2515\n”);
return false;
}

return true;
}

/ Transmit CAN message */
uint8_t CANSPI_Transmit(uCAN_MSG tempCanMsg)
{
uint8_t returnValue = 0;

idReg.tempSIDH = 0;
idReg.tempSIDL = 0;
idReg.tempEID8 = 0;
idReg.tempEID0 = 0;

ctrlStatus.ctrl_status = MCP2515_ReadStatus();

/ Finding empty buffer /
if (ctrlStatus.TXB0REQ != 1)
{
/ convert CAN ID for register /
convertCANid2Reg(tempCanMsg->frame.id, tempCanMsg->frame.idType, &idReg);

/ Load data to Tx Buffer /
MCP2515_LoadTxSequence(MCP2515_LOAD_TXB0SIDH, &(idReg.tempSIDH), tempCanMsg->frame.dlc, &(tempCanMsg->frame.data0));

/ Request to transmit */
MCP2515_RequestToSend(MCP2515_RTS_TX0);

returnValue = 1;
}
else if (ctrlStatus.TXB1REQ != 1)
{
convertCANid2Reg(tempCanMsg->frame.id, tempCanMsg->frame.idType, &idReg);

MCP2515_LoadTxSequence(MCP2515_LOAD_TXB1SIDH, &(idReg.tempSIDH), tempCanMsg->frame.dlc, &(tempCanMsg->frame.data0));
MCP2515_RequestToSend(MCP2515_RTS_TX1);

returnValue = 1;
}
else if (ctrlStatus.TXB2REQ != 1)
{
convertCANid2Reg(tempCanMsg->frame.id, tempCanMsg->frame.idType, &idReg);

MCP2515_LoadTxSequence(MCP2515_LOAD_TXB2SIDH, &(idReg.tempSIDH), tempCanMsg->frame.dlc, &(tempCanMsg->frame.data0));
MCP2515_RequestToSend(MCP2515_RTS_TX2);

returnValue = 1;
}

return (returnValue);
}
uint8_t CANSPI_ReceiveTest(uCAN_MSG tempCanMsg)
{
tempCanMsg->frame.idType = (uint8_t) dEXTENDED_CAN_MSG_ID_2_0B;
tempCanMsg->frame.id = convertReg2ExtendedCANid(18, 20, 22, 255);
tempCanMsg->frame.dlc = 80;
tempCanMsg->frame.data0 = 78;
tempCanMsg->frame.data1 = 76;
tempCanMsg->frame.data2 = 75;
tempCanMsg->frame.data3 = 72;
tempCanMsg->frame.data4 = 70;
tempCanMsg->frame.data5 = 68;
tempCanMsg->frame.data6 = 65;
tempCanMsg->frame.data7 = 63;
return 1;
}
/ Receive CAN message */
uint8_t CANSPI_Receive(uCAN_MSG tempCanMsg)
{
uint8_t returnValue = 0;
rx_reg_t rxReg;
ctrl_rx_status_t rxStatus;

rxStatus.ctrl_rx_status = MCP2515_GetRxStatus();

/ Check receive buffer /
if (rxStatus.rxBuffer != 0)
{
/ Ищем буфер, в котором сообщение /
if ((rxStatus.rxBuffer == MSG_IN_RXB0)|(rxStatus.rxBuffer == MSG_IN_BOTH_BUFFERS))
{
MCP2515_ReadRxSequence(MCP2515_READ_RXB0SIDH, rxReg.rx_reg_array, sizeof(rxReg.rx_reg_array));
}
else if (rxStatus.rxBuffer == MSG_IN_RXB1)
{
MCP2515_ReadRxSequence(MCP2515_READ_RXB1SIDH, rxReg.rx_reg_array, sizeof(rxReg.rx_reg_array));
}

/ Если сообщение CAN расширенного типа /
if (rxStatus.msgType == dEXTENDED_CAN_MSG_ID_2_0B)
{
tempCanMsg->frame.idType = (uint8_t) dEXTENDED_CAN_MSG_ID_2_0B;
tempCanMsg->frame.id = convertReg2ExtendedCANid(rxReg.RXBnEID8, rxReg.RXBnEID0, rxReg.RXBnSIDH, rxReg.RXBnSIDL);
}
else
{
/ Стандартного типа /
tempCanMsg->frame.idType = (uint8_t) dSTANDARD_CAN_MSG_ID_2_0B;
tempCanMsg->frame.id = convertReg2StandardCANid(rxReg.RXBnSIDH, rxReg.RXBnSIDL);
}

tempCanMsg->frame.dlc = rxReg.RXBnDLC;
tempCanMsg->frame.data0 = rxReg.RXBnD0;
tempCanMsg->frame.data1 = rxReg.RXBnD1;
tempCanMsg->frame.data2 = rxReg.RXBnD2;
tempCanMsg->frame.data3 = rxReg.RXBnD3;
tempCanMsg->frame.data4 = rxReg.RXBnD4;
tempCanMsg->frame.data5 = rxReg.RXBnD5;
tempCanMsg->frame.data6 = rxReg.RXBnD6;
tempCanMsg->frame.data7 = rxReg.RXBnD7;

returnValue = 1;
}

return (returnValue);
}

/ check message buffer and return count /
uint8_t CANSPI_messagesInBuffer(void)
{
uint8_t messageCount = 0;

ctrlStatus.ctrl_status = MCP2515_ReadStatus();

if(ctrlStatus.RX0IF != 0)
{
messageCount++;
}

if(ctrlStatus.RX1IF != 0)
{
messageCount++;
}

return (messageCount);
}

/ check BUS off /
uint8_t CANSPI_isBussOff(void)
{
uint8_t returnValue = 0;

errorStatus.error_flag_reg = MCP2515_ReadByte(MCP2515_EFLG);

if(errorStatus.TXBO == 1)
{
returnValue = 1;
}

return (returnValue);
}

/ check Rx Passive Error /
uint8_t CANSPI_isRxErrorPassive(void)
{
uint8_t returnValue = 0;

errorStatus.error_flag_reg = MCP2515_ReadByte(MCP2515_EFLG);

if(errorStatus.RXEP == 1)
{
returnValue = 1;
}

return (returnValue);
}

/ check Tx Passive Error /
uint8_t CANSPI_isTxErrorPassive(void)
{
uint8_t returnValue = 0;

errorStatus.error_flag_reg = MCP2515_ReadByte(MCP2515_EFLG);

if(errorStatus.TXEP == 1)
{
returnValue = 1;
}

return (returnValue);
}

/ convert register value to extended CAN ID /
static uint32_t convertReg2ExtendedCANid(uint8_t tempRXBn_EIDH, uint8_t tempRXBn_EIDL, uint8_t tempRXBn_SIDH, uint8_t tempRXBn_SIDL)
{
uint32_t returnValue = 0;
uint32_t ConvertedID = 0;
uint8_t CAN_standardLo_ID_lo2bits;
uint8_t CAN_standardLo_ID_hi3bits;

CAN_standardLo_ID_lo2bits = (tempRXBn_SIDL & 0x03);
CAN_standardLo_ID_hi3bits = (tempRXBn_SIDL >> 5);
ConvertedID = (tempRXBn_SIDH << 3);
ConvertedID = ConvertedID + CAN_standardLo_ID_hi3bits;
ConvertedID = (ConvertedID << 2);
ConvertedID = ConvertedID + CAN_standardLo_ID_lo2bits;
ConvertedID = (ConvertedID << 8);
ConvertedID = ConvertedID + tempRXBn_EIDH;
ConvertedID = (ConvertedID << 8);
ConvertedID = ConvertedID + tempRXBn_EIDL;
returnValue = ConvertedID;
return (returnValue);
}

/ convert register value to standard CAN ID /
static uint32_t convertReg2StandardCANid(uint8_t tempRXBn_SIDH, uint8_t tempRXBn_SIDL)
{
uint32_t returnValue = 0;
uint32_t ConvertedID;

ConvertedID = (tempRXBn_SIDH << 3);
ConvertedID = ConvertedID + (tempRXBn_SIDL >> 5);
returnValue = ConvertedID;

return (returnValue);
}

/ convert CAN ID to register value */
static void convertCANid2Reg(uint32_t tempPassedInID, uint8_t canIdType, id_reg_t *passedIdReg)
{
uint8_t wipSIDL = 0;

if (canIdType == dEXTENDED_CAN_MSG_ID_2_0B)
{
//EID0
passedIdReg->tempEID0 = 0xFF & tempPassedInID;
tempPassedInID = tempPassedInID >> 8;

//EID8
passedIdReg->tempEID8 = 0xFF & tempPassedInID;
tempPassedInID = tempPassedInID >> 8;

//SIDL
wipSIDL = 0x03 & tempPassedInID;
tempPassedInID = tempPassedInID << 3;
wipSIDL = (0xE0 & tempPassedInID) + wipSIDL;
wipSIDL = wipSIDL + 0x08;
passedIdReg->tempSIDL = 0xEB & wipSIDL;

//SIDH
tempPassedInID = tempPassedInID >> 8;
passedIdReg->tempSIDH = 0xFF & tempPassedInID;
}
else
{
passedIdReg->tempEID8 = 0;
passedIdReg->tempEID0 = 0;
tempPassedInID = tempPassedInID << 5;
passedIdReg->tempSIDL = 0xFF & tempPassedInID;
tempPassedInID = tempPassedInID >> 8;
passedIdReg->tempSIDH = 0xFF & tempPassedInID;
}
}