Initial commit

Using Arduino Framework
This commit is contained in:
Aadi Desai 2022-03-09 12:06:26 +00:00
commit 848cb41aca
No known key found for this signature in database
GPG key ID: CFFFE425830EF4D9
13 changed files with 532 additions and 0 deletions

2
.gitattributes vendored Normal file
View file

@ -0,0 +1,2 @@
# Auto detect text files and perform LF normalization
* text=auto

36
.gitignore vendored Normal file
View file

@ -0,0 +1,36 @@
# Prerequisites
*.d
# Compiled Object files
*.slo
*.lo
*.o
*.obj
# Precompiled Headers
*.gch
*.pch
# Compiled Dynamic libraries
*.so
*.dylib
*.dll
# Fortran module files
*.mod
*.smod
# Compiled Static libraries
*.lai
*.la
*.a
*.lib
# Executables
*.exe
*.out
*.app
# Project Folders
.pio
.vscode

BIN
docs/InstrPart1.pdf Normal file

Binary file not shown.

BIN
docs/InstrPart2.pdf Normal file

Binary file not shown.

BIN
docs/MarkScheme.pdf Normal file

Binary file not shown.

BIN
docs/Spec.pdf Normal file

Binary file not shown.

39
include/README Normal file
View file

@ -0,0 +1,39 @@
This directory is intended for project header files.
A header file is a file containing C declarations and macro definitions
to be shared between several project source files. You request the use of a
header file in your project source file (C, C++, etc) located in `src` folder
by including it, with the C preprocessing directive `#include'.
```src/main.c
#include "header.h"
int main (void)
{
...
}
```
Including a header file produces the same results as copying the header file
into each source file that needs it. Such copying would be time-consuming
and error-prone. With a header file, the related declarations appear
in only one place. If they need to be changed, they can be changed in one
place, and programs that include the header file will automatically use the
new version when next recompiled. The header file eliminates the labor of
finding and changing all the copies as well as the risk that a failure to
find one copy will result in inconsistencies within a program.
In C, the usual convention is to give header files names that end with `.h'.
It is most portable to use only letters, digits, dashes, and underscores in
header file names, and at most one dot.
Read more about using header files in official GCC documentation:
* Include Syntax
* Include Operation
* Once-Only Headers
* Computed Includes
https://gcc.gnu.org/onlinedocs/cpp/Header-Files.html

214
lib/ES_CAN/ES_CAN.cpp Normal file
View file

@ -0,0 +1,214 @@
#include <stm32l4xx_hal_can.h>
#include <stm32l4xx_hal_rcc.h>
#include <stm32l4xx_hal_gpio.h>
#include <stm32l4xx_hal_cortex.h>
//Overwrite the weak default IRQ Handlers and callabcks
extern "C" void CAN1_RX0_IRQHandler(void);
extern "C" void CAN1_TX_IRQHandler(void);
//Pointer to user ISRS
void (*CAN_RX_ISR)() = NULL;
void (*CAN_TX_ISR)() = NULL;
//CAN handle struct with initialisation parameters
//Timing from http://www.bittiming.can-wiki.info/ with bit rate = 125kHz and clock frequency = 80MHz
CAN_HandleTypeDef CAN_Handle = {
CAN1,
{
40, //Prescaler
CAN_MODE_NORMAL, //Normal/loopback/silent mode
CAN_SJW_2TQ, //SyncJumpWidth
CAN_BS1_13TQ, //TimeSeg1
CAN_BS2_2TQ, //TimeSeg2
DISABLE, //TimeTriggeredMode
DISABLE, //AutoBusOff
ENABLE, //AutoWakeUp
ENABLE, //AutoRetransmission
DISABLE, //ReceiveFifoLocked
ENABLE //TransmitFifoPriority
},
HAL_CAN_STATE_RESET, //State
HAL_CAN_ERROR_NONE //Error Code
};
//Initialise CAN dependencies: GPIO and clock
void HAL_CAN_MspInit(CAN_HandleTypeDef* CAN_Handle) {
//Set up the pin initialisation
GPIO_InitTypeDef GPIO_InitCAN_TX = {
GPIO_PIN_12, //PA12 is CAN TX
GPIO_MODE_AF_PP, //Alternate function, push-pull driver
GPIO_NOPULL, //No pull-up
GPIO_SPEED_FREQ_MEDIUM, //Medium slew rate
GPIO_AF9_CAN1 //Alternate function is CAN
};
GPIO_InitTypeDef GPIO_InitCAN_RX = {
GPIO_PIN_11, //PA11 is CAN RX
GPIO_MODE_AF_PP, //Alternate function, push-pull driver
GPIO_PULLUP, //Pull-up enabled
GPIO_SPEED_FREQ_MEDIUM, //Medium slew rate
GPIO_AF9_CAN1 //Alternate function is CAN
};
//Enable the CAN and GPIO clocks
__HAL_RCC_CAN1_CLK_ENABLE(); //Enable the CAN interface clock
__HAL_RCC_GPIOA_CLK_ENABLE(); //Enable the clock for the CAN GPIOs
//Initialise the pins
HAL_GPIO_Init(GPIOA, &GPIO_InitCAN_TX); //Configure CAN pin
HAL_GPIO_Init(GPIOA, &GPIO_InitCAN_RX); //Configure CAN pin
}
uint32_t CAN_Init(bool loopback=false) {
if (loopback)
CAN_Handle.Init.Mode = CAN_MODE_LOOPBACK;
return (uint32_t) HAL_CAN_Init(&CAN_Handle);
}
uint32_t setCANFilter(uint32_t filterID, uint32_t maskID, uint32_t filterBank) {
//Set up the filter definition
CAN_FilterTypeDef filterInfo = {
(filterID << 5) & 0xffe0, //Filter ID
0, //Filter ID LSBs = 0
(maskID << 5) & 0xffe0, //Mask MSBs
0, //Mask LSBs = 0
0, //FIFO selection
filterBank & 0xf, //Filter bank selection
CAN_FILTERMODE_IDMASK, //Mask mode
CAN_FILTERSCALE_32BIT, //32 bit IDs
CAN_FILTER_ENABLE, //Enable filter
0 //uint32_t SlaveStartFilterBank
};
return (uint32_t) HAL_CAN_ConfigFilter(&CAN_Handle, &filterInfo);
}
uint32_t CAN_Start() {
return (uint32_t) HAL_CAN_Start(&CAN_Handle);
}
uint32_t CAN_TX(uint32_t ID, uint8_t data[8]) {
//Set up the message header
CAN_TxHeaderTypeDef txHeader = {
ID & 0x7ff, //Standard ID
0, //Ext ID = 0
CAN_ID_STD, //Use Standard ID
CAN_RTR_DATA, //Data Frame
8, //Send 8 bytes
DISABLE //No time triggered mode
};
//Wait for free mailbox
while (!HAL_CAN_GetTxMailboxesFreeLevel(&CAN_Handle));
//Start the transmission
return (uint32_t) HAL_CAN_AddTxMessage(&CAN_Handle, &txHeader, data, NULL);
}
uint32_t CAN_CheckRXLevel() {
return HAL_CAN_GetRxFifoFillLevel(&CAN_Handle, 0);
}
uint32_t CAN_RX(uint32_t &ID, uint8_t data[8]) {
CAN_RxHeaderTypeDef rxHeader;
//Wait for message in FIFO
while (!HAL_CAN_GetRxFifoFillLevel(&CAN_Handle, 0));
//Get the message from the FIFO
uint32_t result = (uint32_t) HAL_CAN_GetRxMessage(&CAN_Handle, 0, &rxHeader, data);
//Store the ID from the header
ID = rxHeader.StdId;
return result;
}
uint32_t CAN_RegisterRX_ISR(void(& callback)()) {
//Store pointer to user ISR
CAN_RX_ISR = &callback;
//Enable message received interrupt in HAL
uint32_t status = (uint32_t) HAL_CAN_ActivateNotification (&CAN_Handle, CAN_IT_RX_FIFO0_MSG_PENDING);
//Switch on the interrupt
HAL_NVIC_SetPriority (CAN1_RX0_IRQn, 6, 0);
HAL_NVIC_EnableIRQ (CAN1_RX0_IRQn);
return status;
}
uint32_t CAN_RegisterTX_ISR(void(& callback)()) {
//Store pointer to user ISR
CAN_TX_ISR = &callback;
//Enable message received interrupt in HAL
uint32_t status = (uint32_t) HAL_CAN_ActivateNotification (&CAN_Handle, CAN_IT_TX_MAILBOX_EMPTY);
//Switch on the interrupt
HAL_NVIC_SetPriority (CAN1_TX_IRQn, 6, 0);
HAL_NVIC_EnableIRQ (CAN1_TX_IRQn);
return status;
}
void HAL_CAN_RxFifo0MsgPendingCallback (CAN_HandleTypeDef * hcan){
//Call the user ISR if it has been registered
if (CAN_RX_ISR)
CAN_RX_ISR();
}
void HAL_CAN_TxMailbox0CompleteCallback (CAN_HandleTypeDef * hcan){
//Call the user ISR if it has been registered
if (CAN_TX_ISR)
CAN_TX_ISR();
}
void HAL_CAN_TxMailbox1CompleteCallback (CAN_HandleTypeDef * hcan){
//Call the user ISR if it has been registered
if (CAN_TX_ISR)
CAN_TX_ISR();
}
void HAL_CAN_TxMailbox2CompleteCallback (CAN_HandleTypeDef * hcan){
//Call the user ISR if it has been registered
if (CAN_TX_ISR)
CAN_TX_ISR();
}
//This is the base ISR at the interrupt vector
void CAN1_RX0_IRQHandler(void){
//Use the HAL interrupt handler
HAL_CAN_IRQHandler(&CAN_Handle);
}
//This is the base ISR at the interrupt vector
void CAN1_TX_IRQHandler(void){
//Use the HAL interrupt handler
HAL_CAN_IRQHandler(&CAN_Handle);
}

24
lib/ES_CAN/ES_CAN.h Normal file
View file

@ -0,0 +1,24 @@
//Initialise the CAN module
uint32_t CAN_Init(bool loopback=false);
//Enable the CAN module
uint32_t CAN_Start();
//Set up a recevie filter
//Defaults to receive everything
uint32_t setCANFilter(uint32_t filterID=0, uint32_t maskID=0, uint32_t filterBank=0);
//Send a message
uint32_t CAN_TX(uint32_t ID, uint8_t data[8]);
//Get the number of received messages
uint32_t CAN_CheckRXLevel();
//Get a received message from the FIFO
uint32_t CAN_RX(uint32_t &ID, uint8_t data[8]);
//Set up an interrupt on received messages
uint32_t CAN_RegisterRX_ISR(void(& callback)());
//Set up an interrupt on transmitted messages
uint32_t CAN_RegisterTX_ISR(void(& callback)());

46
lib/README Normal file
View file

@ -0,0 +1,46 @@
This directory is intended for project specific (private) libraries.
PlatformIO will compile them to static libraries and link into executable file.
The source code of each library should be placed in a an own separate directory
("lib/your_library_name/[here are source files]").
For example, see a structure of the following two libraries `Foo` and `Bar`:
|--lib
| |
| |--Bar
| | |--docs
| | |--examples
| | |--src
| | |- Bar.c
| | |- Bar.h
| | |- library.json (optional, custom build options, etc) https://docs.platformio.org/page/librarymanager/config.html
| |
| |--Foo
| | |- Foo.c
| | |- Foo.h
| |
| |- README --> THIS FILE
|
|- platformio.ini
|--src
|- main.c
and a contents of `src/main.c`:
```
#include <Foo.h>
#include <Bar.h>
int main (void)
{
...
}
```
PlatformIO Library Dependency Finder will find automatically dependent
libraries scanning project source files.
More information about PlatformIO Library Dependency Finder
- https://docs.platformio.org/page/librarymanager/ldf.html

17
platformio.ini Normal file
View file

@ -0,0 +1,17 @@
; PlatformIO Project Configuration File
;
; Build options: build flags, source filter
; Upload options: custom upload port, speed and extra flags
; Library options: dependencies, extra library storages
; Advanced options: extra scripting
;
; Please visit documentation for the other options and examples
; https://docs.platformio.org/page/projectconf.html
[env:nucleo_l432kc]
platform = ststm32
board = nucleo_l432kc
framework = arduino
monitor_speed = 115200
lib_deps =
olikraus/U8g2@^2.32.10

143
src/main.cpp Normal file
View file

@ -0,0 +1,143 @@
// https://github.com/adamb314/ThreadHandler
#include <Arduino.h>
#include <U8g2lib.h>
#pragma region Config Values
const uint32_t interval = 10; // Display update interval
#pragma endregion
#pragma region Pin Definitions
// Row select and enable
const int RA0_PIN = D3;
const int RA1_PIN = D6;
const int RA2_PIN = D12;
const int REN_PIN = A5;
// Matrix input and output
const int C0_PIN = A2;
const int C1_PIN = D9;
const int C2_PIN = A6;
const int C3_PIN = D1;
const int OUT_PIN = D11;
// Audio analogue out
const int OUTL_PIN = A4;
const int OUTR_PIN = A3;
// Joystick analogue in
const int JOYY_PIN = A0;
const int JOYX_PIN = A1;
// Output multiplexer bits
const int DEN_BIT = 3;
const int DRST_BIT = 4;
const int HKOW_BIT = 5;
const int HKOE_BIT = 6;
#pragma endregion
U8G2_SSD1305_128X32_NONAME_F_HW_I2C u8g2(U8G2_R0); // Display driver object
// Function to set outputs using key matrix
void setOutMuxBit(const uint8_t bitIdx, const bool value) {
digitalWrite(REN_PIN, LOW);
digitalWrite(RA0_PIN, bitIdx & 0x01);
digitalWrite(RA1_PIN, bitIdx & 0x02);
digitalWrite(RA2_PIN, bitIdx & 0x04);
digitalWrite(OUT_PIN, value);
digitalWrite(REN_PIN, HIGH);
delayMicroseconds(2);
digitalWrite(REN_PIN, LOW);
}
// Function to read keys from key matrix
uint32_t readKeys() {
uint32_t keys = 0;
for (uint8_t i = 0; i < 3; i++) {
digitalWrite(REN_PIN, LOW);
digitalWrite(RA0_PIN, i & 0x01);
digitalWrite(RA1_PIN, i & 0x02);
digitalWrite(RA2_PIN, i & 0x04);
digitalWrite(REN_PIN, HIGH);
delayMicroseconds(5);
keys |= !digitalRead(C0_PIN) << (i * 4);
keys |= !digitalRead(C1_PIN) << (i * 4 + 1);
keys |= !digitalRead(C2_PIN) << (i * 4 + 2);
keys |= !digitalRead(C3_PIN) << (i * 4 + 3);
}
digitalWrite(REN_PIN, LOW);
return keys;
}
// Read key values in currently set row
uint8_t readCols() {
uint8_t row = 0;
row |= !digitalRead(C0_PIN) << 0;
row |= !digitalRead(C1_PIN) << 1;
row |= !digitalRead(C2_PIN) << 2;
row |= !digitalRead(C3_PIN) << 3;
return row;
}
// Set current row
void setRow(const uint8_t rowIdx) {
digitalWrite(REN_PIN, LOW);
digitalWrite(RA0_PIN, rowIdx & 0x01);
digitalWrite(RA1_PIN, rowIdx & 0x02);
digitalWrite(RA2_PIN, rowIdx & 0x04);
digitalWrite(REN_PIN, HIGH);
}
void setup() {
#pragma region Pin Setup
pinMode(RA0_PIN, OUTPUT);
pinMode(RA1_PIN, OUTPUT);
pinMode(RA2_PIN, OUTPUT);
pinMode(REN_PIN, OUTPUT);
pinMode(OUT_PIN, OUTPUT);
pinMode(OUTL_PIN, OUTPUT);
pinMode(OUTR_PIN, OUTPUT);
pinMode(LED_BUILTIN, OUTPUT);
pinMode(C0_PIN, INPUT);
pinMode(C1_PIN, INPUT);
pinMode(C2_PIN, INPUT);
pinMode(C3_PIN, INPUT);
pinMode(JOYX_PIN, INPUT);
pinMode(JOYY_PIN, INPUT);
#pragma endregion
#pragma region Display Setup
setOutMuxBit(DRST_BIT, LOW); // Assert display logic reset
delayMicroseconds(2);
setOutMuxBit(DRST_BIT, HIGH); // Release display logic reset
u8g2.begin();
setOutMuxBit(DEN_BIT, HIGH); // Enable display power supply
#pragma endregion
// Initialise UART
Serial.begin(115200);
Serial.println("Hello World");
}
void loop() {
static uint32_t next = millis();
static uint8_t keyArray[7];
for (uint8_t i = 0; i < 3; i++) {
setRow(i);
delayMicroseconds(3);
keyArray[i] = readCols();
}
for (uint8_t i = 3; i < 7; i++) {
setRow(i);
delayMicroseconds(3);
keyArray[i] = readCols();
}
if (millis() > next) {
next += interval;
u8g2.clearBuffer(); // clear the internal memory
u8g2.setFont(u8g2_font_profont12_mf); // choose a suitable font
u8g2.drawStr(2, 10, "Hello World!"); // write something to the internal memory
digitalToggle(LED_BUILTIN);
u8g2.setCursor(2, 20);
for (uint8_t i = 0; i < 7; i++) {
u8g2.print(keyArray[i], HEX);
}
u8g2.sendBuffer(); // transfer internal memory to the display
}
}

11
test/README Normal file
View file

@ -0,0 +1,11 @@
This directory is intended for PlatformIO Unit Testing and project tests.
Unit Testing is a software testing method by which individual units of
source code, sets of one or more MCU program modules together with associated
control data, usage procedures, and operating procedures, are tested to
determine whether they are fit for use. Unit testing finds problems early
in the development cycle.
More information about PlatformIO Unit Testing:
- https://docs.platformio.org/page/plus/unit-testing.html