Add sourcecode from olimex
This commit is contained in:
parent
c7a35d033e
commit
2182e9aba7
5
red/mod-io2/.gitignore
vendored
Normal file
5
red/mod-io2/.gitignore
vendored
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
.pio
|
||||||
|
.vscode/.browse.c_cpp.db*
|
||||||
|
.vscode/c_cpp_properties.json
|
||||||
|
.vscode/launch.json
|
||||||
|
.vscode/ipch
|
16
red/mod-io2/README.txt
Normal file
16
red/mod-io2/README.txt
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
### Install - packages
|
||||||
|
apt-get install build-essential libnewlib-dev gcc-riscv64-unknown-elf libusb-1.0-0-dev libudev-dev gdb-multiarch
|
||||||
|
|
||||||
|
### Install - Visual Studio Code
|
||||||
|
https://code.visualstudio.com/docs/setup/linux
|
||||||
|
|
||||||
|
### Install - Platform IO
|
||||||
|
https://platformio.org/install/ide?install=vscode
|
||||||
|
|
||||||
|
### Install - CH32V-Platform
|
||||||
|
https://github.com/Community-PIO-CH32V/ch32-pio-projects?tab=readme-ov-file#installing-the-ch32v-platform
|
||||||
|
|
||||||
|
To build firmware.bin and firmware.elf select
|
||||||
|
> PlatformIO > PROJECT TASKS > Default > Advanced > Verbose build
|
||||||
|
|
||||||
|
firmware.bin and firmware.elf are located in .pio/build/genericCH32V003F4P6/
|
39
red/mod-io2/include/README
Normal file
39
red/mod-io2/include/README
Normal 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
|
46
red/mod-io2/lib/README
Normal file
46
red/mod-io2/lib/README
Normal 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 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
|
16
red/mod-io2/platformio.ini
Normal file
16
red/mod-io2/platformio.ini
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
; 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:genericCH32V003F4P6]
|
||||||
|
platform = ch32v
|
||||||
|
board = genericCH32V003F4P6
|
||||||
|
framework = noneos-sdk
|
||||||
|
build_flags = -D SYSCLK_FREQ_48MHZ_HSI #-D LOD_DEBUG_ENABLE
|
||||||
|
monitor_speed = 115200
|
103
red/mod-io2/src/adc.c
Normal file
103
red/mod-io2/src/adc.c
Normal file
|
@ -0,0 +1,103 @@
|
||||||
|
#include "adc.h"
|
||||||
|
#include "uptime.h"
|
||||||
|
|
||||||
|
static uint8_t ADC_Ready = 0;
|
||||||
|
|
||||||
|
ADC_Error_Type ADC_Channel_Init(GPIO_TypeDef *GPIO_port, uint16_t GPIO_pin, uint32_t timeoutMS) {
|
||||||
|
ADC_InitTypeDef ADC_InitStructure = {0};
|
||||||
|
|
||||||
|
RCC_ADCCLKConfig(RCC_PCLK2_Div2);
|
||||||
|
|
||||||
|
ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;
|
||||||
|
ADC_InitStructure.ADC_ScanConvMode = DISABLE;
|
||||||
|
ADC_InitStructure.ADC_ContinuousConvMode = DISABLE;
|
||||||
|
ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;
|
||||||
|
ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;
|
||||||
|
ADC_InitStructure.ADC_NbrOfChannel = 1;
|
||||||
|
ADC_Init(ADC1, &ADC_InitStructure);
|
||||||
|
|
||||||
|
ADC_Calibration_Vol(ADC1, ADC_CALVOL_75PERCENT);
|
||||||
|
ADC_Cmd(ADC1, ENABLE);
|
||||||
|
|
||||||
|
return ADC_Calibrate(timeoutMS);
|
||||||
|
}
|
||||||
|
|
||||||
|
ADC_Error_Type ADC_Calibrate(uint32_t timeoutMS) {
|
||||||
|
ADC_Ready = 0;
|
||||||
|
|
||||||
|
ADC_ResetCalibration(ADC1);
|
||||||
|
|
||||||
|
uint32_t time = Uptime_Ms();
|
||||||
|
while(ADC_GetResetCalibrationStatus(ADC1)) {
|
||||||
|
if ((uint32_t)(Uptime_Ms() - time) >= timeoutMS) {
|
||||||
|
// timeout
|
||||||
|
return ADC_ERROR;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ADC_StartCalibration(ADC1);
|
||||||
|
|
||||||
|
time = Uptime_Ms();
|
||||||
|
while(ADC_GetCalibrationStatus(ADC1)){
|
||||||
|
if ((uint32_t)(Uptime_Ms() - time) >= timeoutMS) {
|
||||||
|
// timeout
|
||||||
|
return ADC_ERROR;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ADC_Ready = 1;
|
||||||
|
return ADC_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
ADC_Error_Type Get_ADC_Val(uint8_t ADC_Channel, uint32_t timeoutMS, uint16_t *ADC_Value) {
|
||||||
|
ADC_Calibrate(timeoutMS);
|
||||||
|
if (!ADC_Ready) {
|
||||||
|
return ADC_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
ADC_RegularChannelConfig(ADC1, ADC_Channel, 1, ADC_SampleTime_241Cycles);
|
||||||
|
ADC_SoftwareStartConvCmd(ADC1, ENABLE);
|
||||||
|
|
||||||
|
uint32_t time = Uptime_Ms();
|
||||||
|
while(!ADC_GetFlagStatus( ADC1, ADC_FLAG_EOC )) {
|
||||||
|
if ((uint32_t)(Uptime_Ms() - time) >= timeoutMS) {
|
||||||
|
//timeout
|
||||||
|
ADC_Cmd(ADC1, DISABLE);
|
||||||
|
return ADC_ERROR;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
*ADC_Value = ADC_GetConversionValue(ADC1);
|
||||||
|
ADC_Cmd(ADC1, DISABLE);
|
||||||
|
|
||||||
|
return ADC_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
ADC_Error_Type Get_ADC_Average(uint8_t ADC_channel, uint32_t timeoutMS, uint8_t count, uint16_t *ADC_Value) {
|
||||||
|
if (!ADC_Ready) {
|
||||||
|
return ADC_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t tmp_val = 0;
|
||||||
|
uint16_t val;
|
||||||
|
|
||||||
|
for(uint8_t t = 0; t < count; t++ ){
|
||||||
|
if (Get_ADC_Val(ADC_channel, timeoutMS, &val) == ADC_ERROR) {
|
||||||
|
return ADC_ERROR;
|
||||||
|
}
|
||||||
|
tmp_val += val;
|
||||||
|
Wait_Ms(5);
|
||||||
|
}
|
||||||
|
|
||||||
|
*ADC_Value = tmp_val / count;
|
||||||
|
|
||||||
|
return ADC_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint16_t ADC_Map(uint16_t value, uint16_t minValue, uint16_t maxValue, float minVoltage, float maxVoltage) {
|
||||||
|
float tmp;
|
||||||
|
tmp = (maxVoltage - minVoltage) / (maxValue - minValue);
|
||||||
|
tmp = tmp * (value - minValue) + minVoltage;
|
||||||
|
tmp = tmp * 1000;
|
||||||
|
return (uint16_t) tmp;
|
||||||
|
}
|
28
red/mod-io2/src/adc.h
Normal file
28
red/mod-io2/src/adc.h
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
#ifndef __ADC_H
|
||||||
|
#define __ADC_H
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <ch32v00x.h>
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
ADC_SUCCESS = 0,
|
||||||
|
ADC_ERROR = 1
|
||||||
|
} ADC_Error_Type;
|
||||||
|
|
||||||
|
#define ADC_TIMEOUT_MS 100
|
||||||
|
|
||||||
|
ADC_Error_Type ADC_Channel_Init(GPIO_TypeDef *port, uint16_t pin, uint32_t timeoutMS);
|
||||||
|
ADC_Error_Type ADC_Calibrate(uint32_t timeoutMS);
|
||||||
|
ADC_Error_Type Get_ADC_Val(uint8_t ADC_Channel, uint32_t timeoutMS, uint16_t *ADC_Value);
|
||||||
|
ADC_Error_Type Get_ADC_Average(uint8_t ADC_channel, uint32_t timeoutMS, uint8_t count, uint16_t *ADC_Value);
|
||||||
|
uint16_t ADC_Map(uint16_t value, uint16_t minValue, uint16_t maxValue, float minVoltage, float maxVoltage);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
#endif /* __ADC_H */
|
105
red/mod-io2/src/flash.c
Normal file
105
red/mod-io2/src/flash.c
Normal file
|
@ -0,0 +1,105 @@
|
||||||
|
#include "flash.h"
|
||||||
|
|
||||||
|
#include <ch32v00x.h>
|
||||||
|
#include <ch32v00x_flash.h>
|
||||||
|
|
||||||
|
// Disable LOG_DEBUG locally
|
||||||
|
#undef LOD_DEBUG_ENABLE
|
||||||
|
#include "log_debug.h"
|
||||||
|
|
||||||
|
/* FLASH Keys */
|
||||||
|
#define RDP_Key ((uint16_t)0x00A5)
|
||||||
|
#define FLASH_KEY1 ((uint32_t)0x45670123)
|
||||||
|
#define FLASH_KEY2 ((uint32_t)0xCDEF89AB)
|
||||||
|
|
||||||
|
/* Delay definition */
|
||||||
|
#define EraseTimeout ((uint32_t)0x000B0000)
|
||||||
|
#define ProgramTimeout ((uint32_t)0x00002000)
|
||||||
|
|
||||||
|
/* Flash Control Register bits */
|
||||||
|
#define CR_PG_Set ((uint32_t)0x00000001)
|
||||||
|
#define CR_PG_Reset ((uint32_t)0xFFFFFFFE)
|
||||||
|
#define CR_PER_Set ((uint32_t)0x00000002)
|
||||||
|
#define CR_PER_Reset ((uint32_t)0xFFFFFFFD)
|
||||||
|
#define CR_MER_Set ((uint32_t)0x00000004)
|
||||||
|
#define CR_MER_Reset ((uint32_t)0xFFFFFFFB)
|
||||||
|
#define CR_OPTPG_Set ((uint32_t)0x00000010)
|
||||||
|
#define CR_OPTPG_Reset ((uint32_t)0xFFFFFFEF)
|
||||||
|
#define CR_OPTER_Set ((uint32_t)0x00000020)
|
||||||
|
#define CR_OPTER_Reset ((uint32_t)0xFFFFFFDF)
|
||||||
|
#define CR_STRT_Set ((uint32_t)0x00000040)
|
||||||
|
#define CR_LOCK_Set ((uint32_t)0x00000080)
|
||||||
|
#define CR_PAGE_PG ((uint32_t)0x00010000)
|
||||||
|
#define CR_PAGE_ER ((uint32_t)0x00020000)
|
||||||
|
#define CR_BUF_LOAD ((uint32_t)0x00040000)
|
||||||
|
#define CR_BUF_RST ((uint32_t)0x00080000)
|
||||||
|
|
||||||
|
static volatile uint16_t *OPTION_BYTES = (uint16_t *)OB_BASE;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Default Option bytes values
|
||||||
|
*
|
||||||
|
* RDPR 0xA5
|
||||||
|
* USER 0x17
|
||||||
|
* Data0 0x00
|
||||||
|
* Data1 0x00
|
||||||
|
* WRPR0 0xFF
|
||||||
|
* WRPR1 0xFF
|
||||||
|
*/
|
||||||
|
|
||||||
|
void FLASH_OB_DEBUG() {
|
||||||
|
LOG_DEBUG(
|
||||||
|
"RDPR 0x%04X\r\n"
|
||||||
|
"USER 0x%04X\r\n"
|
||||||
|
"Data0 0x%04X\r\n"
|
||||||
|
"Data1 0x%04X\r\n"
|
||||||
|
"WRPR0 0x%04X\r\n"
|
||||||
|
"WRPR1 0x%04X\r\n"
|
||||||
|
"\r\n",
|
||||||
|
OB->RDPR,
|
||||||
|
OB->USER,
|
||||||
|
OB->Data0,
|
||||||
|
OB->Data1,
|
||||||
|
OB->WRPR0,
|
||||||
|
OB->WRPR1
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t FLASH_OptionByteGet(FLASH_OptionByte_Type byte) {
|
||||||
|
uint16_t data = OPTION_BYTES[(uint8_t)byte];
|
||||||
|
return (uint8_t)(data & 0xFF);
|
||||||
|
}
|
||||||
|
|
||||||
|
FLASH_Status FLASH_OptionByteSet(FLASH_OptionByte_Type byte, uint8_t data) {
|
||||||
|
volatile uint16_t buff[OB_COUNT];
|
||||||
|
// Keeo old values
|
||||||
|
for (uint8_t i = 0; i < OB_COUNT; i++) {
|
||||||
|
buff[i] = OPTION_BYTES[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set Data0
|
||||||
|
buff[byte] = data;
|
||||||
|
|
||||||
|
// Erase
|
||||||
|
FLASH_EraseOptionBytes();
|
||||||
|
|
||||||
|
// Write
|
||||||
|
FLASH_Status status = FLASH_COMPLETE;
|
||||||
|
|
||||||
|
FLASH->OBKEYR = FLASH_KEY1;
|
||||||
|
FLASH->OBKEYR = FLASH_KEY2;
|
||||||
|
status = FLASH_WaitForLastOperation(ProgramTimeout);
|
||||||
|
|
||||||
|
for (uint8_t i = 0; i < OB_COUNT; i++) {
|
||||||
|
if (status == FLASH_COMPLETE) {
|
||||||
|
FLASH->CTLR |= CR_OPTPG_Set;
|
||||||
|
OPTION_BYTES[i] = buff[i];
|
||||||
|
status = FLASH_WaitForLastOperation(ProgramTimeout);
|
||||||
|
if (status != FLASH_TIMEOUT) {
|
||||||
|
FLASH->CTLR &= CR_OPTPG_Reset;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return status;
|
||||||
|
}
|
32
red/mod-io2/src/flash.h
Normal file
32
red/mod-io2/src/flash.h
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
#ifndef __FLASH_H
|
||||||
|
#define __FLASH_H
|
||||||
|
|
||||||
|
#include <ch32v00x_flash.h>
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
OB_RDPR = 0,
|
||||||
|
OB_USER,
|
||||||
|
OB_Data0,
|
||||||
|
OB_Data1,
|
||||||
|
OB_WRPR0,
|
||||||
|
OB_WRPR1,
|
||||||
|
|
||||||
|
OB_COUNT
|
||||||
|
} FLASH_OptionByte_Type;
|
||||||
|
|
||||||
|
void FLASH_OB_DEBUG();
|
||||||
|
|
||||||
|
uint8_t FLASH_OptionByteGet(FLASH_OptionByte_Type byte);
|
||||||
|
FLASH_Status FLASH_OptionByteSet(FLASH_OptionByte_Type byte, uint8_t data);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /* __FLASH_H */
|
248
red/mod-io2/src/i2c_slave.c
Normal file
248
red/mod-io2/src/i2c_slave.c
Normal file
|
@ -0,0 +1,248 @@
|
||||||
|
/*
|
||||||
|
* Code for using the I2C peripheral in slave mode
|
||||||
|
* for OLIMEX Neo6502PC-PWR board
|
||||||
|
*
|
||||||
|
* Project is based on https://github.com/cnlohr/ch32v003fun/blob/master/examples/i2c_slave/
|
||||||
|
*
|
||||||
|
* MIT License
|
||||||
|
*
|
||||||
|
* Copyright (c) 2024 Renze Nicolai
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
|
* copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
* SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "i2c_slave.h"
|
||||||
|
|
||||||
|
// Disable LOG_DEBUG locally
|
||||||
|
#undef LOD_DEBUG_ENABLE
|
||||||
|
#include "log_debug.h"
|
||||||
|
|
||||||
|
static uint8_t _address = 0x00;
|
||||||
|
static uint8_t _first_write = 1;
|
||||||
|
static uint8_t _register = 0;
|
||||||
|
static uint8_t _offset = 0;
|
||||||
|
static uint8_t _writing = 0;
|
||||||
|
|
||||||
|
static i2c_register_callback_t _register_callback = NULL;
|
||||||
|
static i2c_write_callback_t _write_callback = NULL;
|
||||||
|
static i2c_stop_callback_t _stop_callback = NULL;
|
||||||
|
static i2c_read_callback_t _read_callback = NULL;
|
||||||
|
|
||||||
|
void I2C_Slave_Begin();
|
||||||
|
uint8_t I2C_Slave_Remap(uint8_t pos);
|
||||||
|
|
||||||
|
void I2C_Slave_Setup(uint8_t addr) {
|
||||||
|
_address = addr;
|
||||||
|
_first_write = 1;
|
||||||
|
_register = 0;
|
||||||
|
_offset = 0;
|
||||||
|
|
||||||
|
I2C_Slave_Begin();
|
||||||
|
}
|
||||||
|
|
||||||
|
void I2C_Slave_SetOnRegister(i2c_register_callback_t on_register) {
|
||||||
|
_register_callback = on_register;
|
||||||
|
}
|
||||||
|
|
||||||
|
void I2C_Slave_SetOnWrite(i2c_write_callback_t on_write) {
|
||||||
|
_write_callback = on_write;
|
||||||
|
}
|
||||||
|
|
||||||
|
void I2C_Slave_SetOnRead(i2c_read_callback_t on_read) {
|
||||||
|
_read_callback = on_read;
|
||||||
|
}
|
||||||
|
|
||||||
|
void I2C_Slave_SetOnStop(i2c_stop_callback_t on_stop) {
|
||||||
|
_stop_callback = on_stop;
|
||||||
|
}
|
||||||
|
|
||||||
|
void I2C_Slave_Begin() {
|
||||||
|
// SDA, SCL - Open-drain multiplexed output
|
||||||
|
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC, ENABLE);
|
||||||
|
GPIO_InitTypeDef GPIO_PortC = {0};
|
||||||
|
GPIO_PortC.GPIO_Pin = GPIO_Pin_1 | GPIO_Pin_2;
|
||||||
|
GPIO_PortC.GPIO_Mode = GPIO_Mode_AF_OD;
|
||||||
|
GPIO_PortC.GPIO_Speed = GPIO_Speed_50MHz;
|
||||||
|
GPIO_Init(GPIOC, &GPIO_PortC);
|
||||||
|
|
||||||
|
// Enable I2C1
|
||||||
|
RCC->APB1PCENR |= RCC_APB1Periph_I2C1;
|
||||||
|
|
||||||
|
// Reset I2C1 to init all regs
|
||||||
|
RCC->APB1PRSTR |= RCC_APB1Periph_I2C1;
|
||||||
|
RCC->APB1PRSTR &= ~RCC_APB1Periph_I2C1;
|
||||||
|
|
||||||
|
I2C1->CTLR1 |= I2C_CTLR1_SWRST;
|
||||||
|
I2C1->CTLR1 &= ~I2C_CTLR1_SWRST;
|
||||||
|
|
||||||
|
// Set module clock frequency
|
||||||
|
uint32_t prerate = 2000000; // I2C Logic clock rate, must be higher than the bus clock rate
|
||||||
|
I2C1->CTLR2 |= (SystemCoreClock / prerate) & I2C_CTLR2_FREQ;
|
||||||
|
|
||||||
|
// Enable interrupts
|
||||||
|
I2C1->CTLR2 |= I2C_CTLR2_ITBUFEN | I2C_CTLR2_ITEVTEN | I2C_CTLR2_ITERREN;
|
||||||
|
|
||||||
|
NVIC_EnableIRQ(I2C1_EV_IRQn); // Event interrupt
|
||||||
|
NVIC_SetPriority(I2C1_EV_IRQn, 2 << 4);
|
||||||
|
|
||||||
|
NVIC_EnableIRQ(I2C1_ER_IRQn); // Error interrupt
|
||||||
|
NVIC_SetPriority(I2C1_ER_IRQn, 2 << 4);
|
||||||
|
|
||||||
|
// Set clock configuration
|
||||||
|
uint32_t clockrate = 1000000; // I2C Bus clock rate, must be lower than the logic clock rate
|
||||||
|
I2C1->CKCFGR = ((SystemCoreClock / (3 * clockrate)) & I2C_CKCFGR_CCR) | I2C_CKCFGR_FS; // Fast mode 33% duty cycle
|
||||||
|
// I2C1->CKCFGR = ((SystemCoreClock/ (25 * clockrate)) & I2C_CKCFGR_CCR) | I2C_CKCFGR_DUTY | I2C_CKCFGR_FS; // Fast mode 36% duty cycle
|
||||||
|
// I2C1->CKCFGR = (SystemCoreClock / (2 * clockrate)) & I2C_CKCFGR_CCR; // Standard mode good to 100kHz
|
||||||
|
|
||||||
|
// Set I2C _address
|
||||||
|
I2C1->OADDR1 = _address << 1;
|
||||||
|
I2C1->OADDR2 = 0;
|
||||||
|
|
||||||
|
// Enable I2C
|
||||||
|
I2C1->CTLR1 |= I2C_CTLR1_PE;
|
||||||
|
|
||||||
|
// Acknowledge bytes when they are received
|
||||||
|
I2C1->CTLR1 |= I2C_CTLR1_ACK;
|
||||||
|
|
||||||
|
// Disable clock stretch
|
||||||
|
// I2C1->CTLR1 |= I2C_CTLR1_NOSTRETCH;
|
||||||
|
|
||||||
|
LOG_DEBUG("I2C_Slave: Initialized @ 0x%02X" EOL, _address);
|
||||||
|
}
|
||||||
|
|
||||||
|
void I2C_Slave_Start_Event() {
|
||||||
|
// LOG_DEBUG("I2C_Slave: Start" EOL);
|
||||||
|
_first_write = 1; // Next write will be the _register
|
||||||
|
_offset = 0; // Reset _offset
|
||||||
|
}
|
||||||
|
|
||||||
|
void I2C_Slave_Write_Event() {
|
||||||
|
// LOG_DEBUG("I2C_Slave: Write" EOL);
|
||||||
|
if (_first_write) {
|
||||||
|
// First byte written, set the _register
|
||||||
|
_register = I2C1->DATAR;
|
||||||
|
_offset = 0;
|
||||||
|
_first_write = 0;
|
||||||
|
_writing = 0;
|
||||||
|
|
||||||
|
if (_register_callback != NULL) {
|
||||||
|
_register_callback(_register);
|
||||||
|
}
|
||||||
|
LOG_DEBUG("I2C_Slave: Register 0x%02X" EOL, _register);
|
||||||
|
} else {
|
||||||
|
// Normal register write
|
||||||
|
_writing = 1;
|
||||||
|
|
||||||
|
uint8_t data = I2C1->DATAR;
|
||||||
|
LOG_DEBUG("I2C_Slave: Write 0x%02X" EOL, data);
|
||||||
|
|
||||||
|
if (_write_callback != NULL) {
|
||||||
|
_write_callback(_register, _offset, data);
|
||||||
|
}
|
||||||
|
_offset++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void I2C_Slave_Read_Event() {
|
||||||
|
// LOG_DEBUG("I2C_Slave: Read" EOL);
|
||||||
|
_writing = 0;
|
||||||
|
|
||||||
|
uint8_t data = (_read_callback == NULL ?
|
||||||
|
0
|
||||||
|
:
|
||||||
|
_read_callback(_register, _offset)
|
||||||
|
);
|
||||||
|
|
||||||
|
I2C1->DATAR = data;
|
||||||
|
_offset++;
|
||||||
|
|
||||||
|
LOG_DEBUG("I2C_Slave: Read 0x%02X" EOL, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
void I2C_Slave_Stop_Event() {
|
||||||
|
LOG_DEBUG("I2C_Slave: Stop" EOL);
|
||||||
|
if (_stop_callback != NULL) {
|
||||||
|
_stop_callback(_register, _offset);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void I2C1_EV_IRQHandler(void) {
|
||||||
|
// LOG_DEBUG("I2C_Slave: Event" EOL);
|
||||||
|
|
||||||
|
uint16_t STAR1, STAR2 __attribute__((unused));
|
||||||
|
STAR1 = I2C1->STAR1;
|
||||||
|
STAR2 = I2C1->STAR2;
|
||||||
|
|
||||||
|
if (STAR1 & I2C_STAR1_ADDR) {
|
||||||
|
// Start event
|
||||||
|
I2C_Slave_Start_Event();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (STAR1 & I2C_STAR1_RXNE) {
|
||||||
|
// Write event
|
||||||
|
I2C_Slave_Write_Event();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (STAR1 & I2C_STAR1_TXE) {
|
||||||
|
// Read event
|
||||||
|
I2C_Slave_Read_Event();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (STAR1 & I2C_STAR1_STOPF) {
|
||||||
|
// Stop event
|
||||||
|
// Clear stop
|
||||||
|
I2C1->CTLR1 &= ~(I2C_CTLR1_STOP);
|
||||||
|
|
||||||
|
I2C_Slave_Stop_Event();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void I2C1_ER_IRQHandler(void) {
|
||||||
|
LOG_DEBUG("I2C_Slave: ");
|
||||||
|
|
||||||
|
uint16_t STAR1 = I2C1->STAR1;
|
||||||
|
|
||||||
|
if (STAR1 & I2C_STAR1_BERR) {
|
||||||
|
// Bus error
|
||||||
|
LOG_DEBUG("Bus error" EOL);
|
||||||
|
|
||||||
|
// Clear error
|
||||||
|
I2C1->STAR1 &= ~(I2C_STAR1_BERR);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (STAR1 & I2C_STAR1_ARLO) {
|
||||||
|
// Arbitration lost error
|
||||||
|
LOG_DEBUG("Arbitration lost error" EOL);
|
||||||
|
|
||||||
|
// Clear error
|
||||||
|
I2C1->STAR1 &= ~(I2C_STAR1_ARLO);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (STAR1 & I2C_STAR1_AF) {
|
||||||
|
if (_writing) {
|
||||||
|
// Acknowledge failure
|
||||||
|
LOG_DEBUG("Acknowledge failure" EOL);
|
||||||
|
} else {
|
||||||
|
LOG_DEBUG("NAC" EOL);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clear error
|
||||||
|
I2C1->STAR1 &= ~(I2C_STAR1_AF);
|
||||||
|
}
|
||||||
|
}
|
62
red/mod-io2/src/i2c_slave.h
Normal file
62
red/mod-io2/src/i2c_slave.h
Normal file
|
@ -0,0 +1,62 @@
|
||||||
|
/*
|
||||||
|
* Header for using the I2C peripheral in slave mode
|
||||||
|
* for OLIMEX Neo6502PC-PWR board
|
||||||
|
*
|
||||||
|
* Project is based on https://github.com/cnlohr/ch32v003fun/blob/master/examples/i2c_slave/
|
||||||
|
*
|
||||||
|
* MIT License
|
||||||
|
*
|
||||||
|
* Copyright (c) 2024 Renze Nicolai
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
|
* copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
* SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef __I2C_SLAVE_H
|
||||||
|
#define __I2C_SLAVE_H
|
||||||
|
|
||||||
|
#include <ch32v00x.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
void I2C1_EV_IRQHandler(void) __attribute__((interrupt("WCH-Interrupt-fast")));
|
||||||
|
void I2C1_EV_IRQHandler(void);
|
||||||
|
|
||||||
|
void I2C1_ER_IRQHandler(void) __attribute__((interrupt("WCH-Interrupt-fast")));
|
||||||
|
void I2C1_ER_IRQHandler(void);
|
||||||
|
|
||||||
|
|
||||||
|
typedef void (*i2c_register_callback_t)(uint8_t reg);
|
||||||
|
typedef void (*i2c_write_callback_t)(uint8_t reg, uint8_t offset, uint8_t data);
|
||||||
|
typedef void (*i2c_stop_callback_t)(uint8_t reg, uint8_t offset);
|
||||||
|
typedef uint8_t (*i2c_read_callback_t)(uint8_t reg, uint8_t offset);
|
||||||
|
|
||||||
|
void I2C_Slave_Setup(uint8_t addr);
|
||||||
|
void I2C_Slave_SetOnRegister(i2c_register_callback_t on_register);
|
||||||
|
void I2C_Slave_SetOnWrite(i2c_write_callback_t on_write);
|
||||||
|
void I2C_Slave_SetOnStop(i2c_stop_callback_t on_stop);
|
||||||
|
void I2C_Slave_SetOnRead(i2c_read_callback_t on_read);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /* __I2C_SLAVE_H */
|
28
red/mod-io2/src/log_debug.c
Normal file
28
red/mod-io2/src/log_debug.c
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
#include <ch32v00x.h>
|
||||||
|
|
||||||
|
#include "log_debug.h"
|
||||||
|
|
||||||
|
void LOG_DEBUG_Configure(uint32_t baudrate, uint16_t stop_bits, uint16_t parity) {
|
||||||
|
#ifdef LOD_DEBUG_ENABLE
|
||||||
|
GPIO_InitTypeDef GPIO_InitStructure = {0};
|
||||||
|
USART_InitTypeDef USART_InitStructure = {0};
|
||||||
|
|
||||||
|
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOD | RCC_APB2Periph_USART1 | RCC_APB2Periph_AFIO, ENABLE);
|
||||||
|
|
||||||
|
/* CH32_UART TX-->D5 */
|
||||||
|
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5;
|
||||||
|
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
|
||||||
|
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
|
||||||
|
GPIO_Init(GPIOD, &GPIO_InitStructure);
|
||||||
|
|
||||||
|
USART_InitStructure.USART_BaudRate = baudrate;
|
||||||
|
USART_InitStructure.USART_StopBits = stop_bits;
|
||||||
|
USART_InitStructure.USART_Parity = parity;
|
||||||
|
USART_InitStructure.USART_WordLength = USART_WordLength_8b;
|
||||||
|
USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
|
||||||
|
USART_InitStructure.USART_Mode = USART_Mode_Tx;
|
||||||
|
|
||||||
|
USART_Init(USART1, &USART_InitStructure);
|
||||||
|
USART_Cmd(USART1, ENABLE);
|
||||||
|
#endif
|
||||||
|
}
|
35
red/mod-io2/src/log_debug.h
Normal file
35
red/mod-io2/src/log_debug.h
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
#ifndef __LOG_DEBUG_H
|
||||||
|
#define __LOG_DEBUG_H
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
#ifdef LOD_DEBUG_ENABLE
|
||||||
|
#define LOG_DEBUG(f_, ...) do { printf((f_), ##__VA_ARGS__); } while(0)
|
||||||
|
#else
|
||||||
|
#define LOG_DEBUG(f_, ...)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define EOL "\r\n"
|
||||||
|
|
||||||
|
#define BYTE_TO_BINARY_PATTERN "%c%c%c%c%c%c%c%c"
|
||||||
|
#define BYTE_TO_BINARY(byte) \
|
||||||
|
((byte) & 0x80 ? '1' : '0'), \
|
||||||
|
((byte) & 0x40 ? '1' : '0'), \
|
||||||
|
((byte) & 0x20 ? '1' : '0'), \
|
||||||
|
((byte) & 0x10 ? '1' : '0'), \
|
||||||
|
((byte) & 0x08 ? '1' : '0'), \
|
||||||
|
((byte) & 0x04 ? '1' : '0'), \
|
||||||
|
((byte) & 0x02 ? '1' : '0'), \
|
||||||
|
((byte) & 0x01 ? '1' : '0')
|
||||||
|
|
||||||
|
void LOG_DEBUG_Configure(uint32_t baudrate, uint16_t stop_bits, uint16_t parity);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /* __LOG_DEBUG_H */
|
45
red/mod-io2/src/main.cpp
Normal file
45
red/mod-io2/src/main.cpp
Normal file
|
@ -0,0 +1,45 @@
|
||||||
|
#include <ch32v00x.h>
|
||||||
|
|
||||||
|
#include "log_debug.h"
|
||||||
|
#include "uptime.h"
|
||||||
|
|
||||||
|
#include "mod_io2.h"
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
void NMI_Handler(void) __attribute__((interrupt("WCH-Interrupt-fast")));
|
||||||
|
void HardFault_Handler(void) __attribute__((interrupt("WCH-Interrupt-fast")));
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
int main(void) {
|
||||||
|
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
|
||||||
|
SystemCoreClockUpdate();
|
||||||
|
|
||||||
|
// Disable GPIO Alternate Functions and extrnal oscilator
|
||||||
|
// Othewize GPIO PORT A does not work
|
||||||
|
RCC_HSEConfig(RCC_HSE_OFF);
|
||||||
|
GPIO_PinRemapConfig(GPIO_Remap_PA1_2, DISABLE);
|
||||||
|
|
||||||
|
Uptime_Init();
|
||||||
|
LOG_DEBUG_Configure(115200, USART_StopBits_1, USART_Parity_No);
|
||||||
|
|
||||||
|
LOG_DEBUG(EOL "Sys Clock: %ld" EOL, SystemCoreClock);
|
||||||
|
|
||||||
|
MOD_IO2::Setup();
|
||||||
|
while (1) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void NMI_Handler(void) {
|
||||||
|
LOG_DEBUG("NMI_Handler" EOL);
|
||||||
|
}
|
||||||
|
|
||||||
|
void HardFault_Handler(void) {
|
||||||
|
LOG_DEBUG("HardFault_Handler" EOL);
|
||||||
|
while (1) {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
438
red/mod-io2/src/mod_io2.cpp
Normal file
438
red/mod-io2/src/mod_io2.cpp
Normal file
|
@ -0,0 +1,438 @@
|
||||||
|
#include "mod_io2.h"
|
||||||
|
|
||||||
|
// Disable LOG_DEBUG locally
|
||||||
|
#undef LOD_DEBUG_ENABLE
|
||||||
|
#include "log_debug.h"
|
||||||
|
|
||||||
|
#include "uptime.h"
|
||||||
|
#include "adc.h"
|
||||||
|
#include "flash.h"
|
||||||
|
|
||||||
|
void MOD_IO2::Setup() {
|
||||||
|
i2c_address = FLASH_OptionByteGet(OB_Data0);
|
||||||
|
Begin();
|
||||||
|
}
|
||||||
|
|
||||||
|
void MOD_IO2::Setup(uint8_t addr) {
|
||||||
|
i2c_address = addr;
|
||||||
|
Begin();
|
||||||
|
}
|
||||||
|
|
||||||
|
void MOD_IO2::Begin() {
|
||||||
|
// Validate I2C address
|
||||||
|
if (i2c_address > 0x7F || i2c_address == 0x00) {
|
||||||
|
i2c_address = MOD_IO2_ADDRESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Enable needed periphery
|
||||||
|
RCC_APB2PeriphClockCmd(
|
||||||
|
RCC_APB2Periph_GPIOA |
|
||||||
|
RCC_APB2Periph_GPIOC |
|
||||||
|
RCC_APB2Periph_GPIOD |
|
||||||
|
RCC_APB2Periph_AFIO |
|
||||||
|
RCC_APB2Periph_ADC1 |
|
||||||
|
RCC_APB2Periph_TIM1,
|
||||||
|
ENABLE
|
||||||
|
);
|
||||||
|
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
|
||||||
|
|
||||||
|
// GPIO configuration
|
||||||
|
// GPIOs
|
||||||
|
for (uint8_t gpio=0; gpio < MOD_IO2_GPIO_COUNT; gpio++) {
|
||||||
|
GPIOConfig(gpio, GPIO_Mode_IN_FLOATING, true);
|
||||||
|
}
|
||||||
|
// Relays
|
||||||
|
for (uint8_t relay=0; relay < MOD_IO2_RELAY_COUNT; relay++) {
|
||||||
|
RelayConfig(relay);
|
||||||
|
}
|
||||||
|
// PGM1
|
||||||
|
GPIO_InitTypeDef cfg = {0};
|
||||||
|
cfg.GPIO_Mode = GPIO_Mode_IPU;
|
||||||
|
cfg.GPIO_Pin = PGM1_JUMPER.pin;
|
||||||
|
cfg.GPIO_Speed = GPIO_Speed_50MHz;
|
||||||
|
GPIO_Init(PGM1_JUMPER.port, &cfg);
|
||||||
|
|
||||||
|
// I2C configuration
|
||||||
|
I2C_Slave_Setup(i2c_address);
|
||||||
|
|
||||||
|
I2C_Slave_SetOnRegister(onRegisterSet);
|
||||||
|
I2C_Slave_SetOnRead(onRegRead);
|
||||||
|
I2C_Slave_SetOnWrite(onRegWrite);
|
||||||
|
|
||||||
|
LOG_DEBUG("Initialized." EOL);
|
||||||
|
}
|
||||||
|
|
||||||
|
void MOD_IO2::onRegisterSet(uint8_t reg) {
|
||||||
|
i2c_reg = NULL;
|
||||||
|
for (uint8_t r = 0; r < MOD_IO2_REG_COUNT; r++) {
|
||||||
|
if (REG_MAP[r].reg == reg) {
|
||||||
|
i2c_reg = ®_MAP[r];
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t MOD_IO2::onRegRead(uint8_t reg, uint8_t offset) {
|
||||||
|
if (i2c_reg == NULL || i2c_reg->onRead == NULL) {
|
||||||
|
return 0x00;
|
||||||
|
}
|
||||||
|
|
||||||
|
return i2c_reg->onRead(reg, offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
void MOD_IO2::onRegWrite(uint8_t reg, uint8_t offset, uint8_t data) {
|
||||||
|
if (i2c_reg == NULL || i2c_reg->onWrite == NULL) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
i2c_reg->onWrite(reg, offset, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t MOD_IO2::onGetBoardID(uint8_t reg, uint8_t offset) {
|
||||||
|
LOG_DEBUG("Board ID 0x%02X" EOL, MOD_IO2_BOARD_ID);
|
||||||
|
return MOD_IO2_BOARD_ID;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t MOD_IO2::onGetVersion(uint8_t reg, uint8_t offset) {
|
||||||
|
uint8_t version = (MOD_IO2_VER_MAJOR << 4) | MOD_IO2_VER_MINOR;
|
||||||
|
LOG_DEBUG("Version 0x%02X" EOL, version);
|
||||||
|
return version;
|
||||||
|
}
|
||||||
|
|
||||||
|
void MOD_IO2::onSetDir(uint8_t reg, uint8_t offset, uint8_t data) {
|
||||||
|
for (uint8_t gpio = 0; gpio < MOD_IO2_GPIO_COUNT; gpio++) {
|
||||||
|
uint8_t mask = (1 << gpio);
|
||||||
|
GPIOConfig(
|
||||||
|
gpio,
|
||||||
|
((data & mask) != 0 ?
|
||||||
|
// Input
|
||||||
|
((gpio_pullup & mask) != 0 ? GPIO_Mode_IPU : GPIO_Mode_IN_FLOATING)
|
||||||
|
:
|
||||||
|
// Outout
|
||||||
|
GPIO_Mode_Out_PP
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
gpio_direction = data;
|
||||||
|
LOG_DEBUG("SetDir " BYTE_TO_BINARY_PATTERN EOL, BYTE_TO_BINARY(gpio_direction));
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t MOD_IO2::onGetDir(uint8_t reg, uint8_t offset) {
|
||||||
|
LOG_DEBUG("GetDir " BYTE_TO_BINARY_PATTERN EOL, BYTE_TO_BINARY(gpio_direction));
|
||||||
|
return gpio_direction;
|
||||||
|
}
|
||||||
|
|
||||||
|
void MOD_IO2::onSetPullUp(uint8_t reg, uint8_t offset, uint8_t data) {
|
||||||
|
// clear pullup bit for outputs
|
||||||
|
gpio_pullup = (data & gpio_direction);
|
||||||
|
|
||||||
|
for (uint8_t gpio = 0; gpio < MOD_IO2_GPIO_COUNT; gpio++) {
|
||||||
|
uint8_t mask = (1 << gpio);
|
||||||
|
if ((gpio_direction & mask) == 0) {
|
||||||
|
// Skip outputs
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
GPIOConfig(gpio, (gpio_pullup & mask) != 0 ? GPIO_Mode_IPU : GPIO_Mode_IN_FLOATING);
|
||||||
|
}
|
||||||
|
LOG_DEBUG("SetPullup " BYTE_TO_BINARY_PATTERN EOL, BYTE_TO_BINARY(gpio_pullup));
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t MOD_IO2::onGetPullUp(uint8_t reg, uint8_t offset) {
|
||||||
|
LOG_DEBUG("GetPullup " BYTE_TO_BINARY_PATTERN EOL, BYTE_TO_BINARY(gpio_pullup));
|
||||||
|
return gpio_pullup;
|
||||||
|
}
|
||||||
|
|
||||||
|
void MOD_IO2::onSetLevel(uint8_t reg, uint8_t offset, uint8_t data) {
|
||||||
|
for (uint8_t gpio = 0; gpio < MOD_IO2_GPIO_COUNT; gpio++) {
|
||||||
|
uint8_t mask = (1 << gpio);
|
||||||
|
// Check if GPIO is output
|
||||||
|
if ((gpio_direction & mask) != 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
GPIOSet(gpio, (BitAction)((data & mask) != 0));
|
||||||
|
}
|
||||||
|
LOG_DEBUG("SetGPIO " BYTE_TO_BINARY_PATTERN EOL, BYTE_TO_BINARY(gpio_state));
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t MOD_IO2::onGetLevel(uint8_t reg, uint8_t offset) {
|
||||||
|
for (uint8_t gpio = 0; gpio < MOD_IO2_GPIO_COUNT; gpio++) {
|
||||||
|
GPIOGet(gpio);
|
||||||
|
}
|
||||||
|
LOG_DEBUG("GetGPIO " BYTE_TO_BINARY_PATTERN EOL, BYTE_TO_BINARY(gpio_state));
|
||||||
|
return gpio_state;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t MOD_IO2::onAnalogGet(uint8_t reg, uint8_t offset) {
|
||||||
|
if (offset == 1) {
|
||||||
|
if (adc_error) {
|
||||||
|
LOG_DEBUG("ADC 0x%02X n/a" EOL, reg);
|
||||||
|
} else {
|
||||||
|
#ifdef MOD_IO2_ADC_VOLTAGE
|
||||||
|
LOG_DEBUG("ADC 0x%02X %d %d mV" EOL, reg, adc_value, adc_voltage);
|
||||||
|
#else
|
||||||
|
LOG_DEBUG("ADC 0x%02X %d" EOL, reg, adc_value);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
return ((adc_value >> 8) & 0xFF);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (offset > 1) {
|
||||||
|
return 0x00;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t gpio = reg & 0x0F;
|
||||||
|
adc_error = false;
|
||||||
|
|
||||||
|
if (GPIO_MAP[gpio].adc == 255) {
|
||||||
|
adc_error = true;
|
||||||
|
} else {
|
||||||
|
GPIOConfig(gpio, GPIO_Mode_AIN);
|
||||||
|
adc_error |= (ADC_SUCCESS != ADC_Channel_Init(GPIO_MAP[gpio].port, GPIO_MAP[gpio].pin, MOD_IO2_ADC_TIMEOUT));
|
||||||
|
adc_error |= (ADC_SUCCESS != Get_ADC_Val(GPIO_MAP[gpio].adc, MOD_IO2_ADC_TIMEOUT, &adc_value));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (adc_error) {
|
||||||
|
adc_value = 0;
|
||||||
|
adc_voltage = 0;
|
||||||
|
}
|
||||||
|
#ifdef MOD_IO2_ADC_VOLTAGE
|
||||||
|
else {
|
||||||
|
adc_voltage = ADC_Map(adc_value, 0, 1023, 0.0, 3.3);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return (adc_value & 0xFF);
|
||||||
|
}
|
||||||
|
|
||||||
|
void MOD_IO2::onSetRelayState(uint8_t reg, uint8_t offset, uint8_t data) {
|
||||||
|
for (uint8_t relay = 0; relay < MOD_IO2_RELAY_COUNT; relay++) {
|
||||||
|
RelaySet(relay, (BitAction)((data & (1 << relay)) != 0));
|
||||||
|
}
|
||||||
|
LOG_DEBUG("SetRelays " BYTE_TO_BINARY_PATTERN EOL, BYTE_TO_BINARY(relay_state));
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t MOD_IO2::onGetRelayState(uint8_t reg, uint8_t offset) {
|
||||||
|
LOG_DEBUG("GetRelays " BYTE_TO_BINARY_PATTERN EOL, BYTE_TO_BINARY(relay_state));
|
||||||
|
return relay_state;
|
||||||
|
}
|
||||||
|
|
||||||
|
void MOD_IO2::onRelayOn(uint8_t reg, uint8_t offset, uint8_t data) {
|
||||||
|
for (uint8_t relay = 0; relay < MOD_IO2_RELAY_COUNT; relay++) {
|
||||||
|
if (((data & (1 << relay)) != 0)) {
|
||||||
|
RelaySet(relay, Bit_SET);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
LOG_DEBUG("RelaysOn " BYTE_TO_BINARY_PATTERN EOL, BYTE_TO_BINARY(relay_state));
|
||||||
|
}
|
||||||
|
|
||||||
|
void MOD_IO2::onRelayOff(uint8_t reg, uint8_t offset, uint8_t data) {
|
||||||
|
for (uint8_t relay = 0; relay < MOD_IO2_RELAY_COUNT; relay++) {
|
||||||
|
if (((data & (1 << relay)) != 0)) {
|
||||||
|
RelaySet(relay, Bit_RESET);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
LOG_DEBUG("RelaysOff " BYTE_TO_BINARY_PATTERN EOL, BYTE_TO_BINARY(relay_state));
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t MOD_IO2::onGetRelayOff(uint8_t reg, uint8_t offset) {
|
||||||
|
return (~relay_state) & MOD_IO2_RELAY_MASK;
|
||||||
|
}
|
||||||
|
|
||||||
|
void MOD_IO2::onSetPWM(uint8_t reg, uint8_t offset, uint8_t data) {
|
||||||
|
uint8_t pwm = reg & 0x0F;
|
||||||
|
if (pwm >= MOD_IO2_PWM_COUNT) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t gpio;
|
||||||
|
if (pwm == 0) {
|
||||||
|
// Disable PWMx where x = data
|
||||||
|
if (data == 0 || data >= MOD_IO2_PWM_COUNT) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
gpio = PWM_MAP[data].gpio;
|
||||||
|
PWMDisable(GPIO_MAP[gpio].tim);
|
||||||
|
GPIOConfig(gpio, GPIO_Mode_IN_FLOATING);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
gpio = PWM_MAP[pwm].gpio;
|
||||||
|
GPIOConfig(gpio, GPIO_Mode_AF_PP);
|
||||||
|
PWMConfig(GPIO_MAP[gpio].tim, GPIO_MAP[gpio].tch, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
void MOD_IO2::onSetAddress(uint8_t reg, uint8_t offset, uint8_t data) {
|
||||||
|
if (GPIO_ReadInputDataBit(PGM1_JUMPER.port, PGM1_JUMPER.pin) == 0) {
|
||||||
|
// Set new address only if PGM1_JUMPER is closed
|
||||||
|
Setup(data);
|
||||||
|
|
||||||
|
FLASH_Unlock();
|
||||||
|
FLASH_OptionByteSet(OB_Data0, i2c_address);
|
||||||
|
FLASH_Lock();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void MOD_IO2::GPIOConfig(uint8_t gpio, GPIOMode_TypeDef mode, bool initial) {
|
||||||
|
// LOG_DEBUG("GPIOConfig(%d)" EOL, gpio);
|
||||||
|
if (gpio >= MOD_IO2_GPIO_COUNT) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t mask = (1 << gpio);
|
||||||
|
|
||||||
|
bool changes_detected = initial || gpio_config[gpio] != mode;
|
||||||
|
|
||||||
|
if (!changes_detected) {
|
||||||
|
// LOG_DEBUG("GPIO %d - NO CHANGES" EOL, gpio);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set registers
|
||||||
|
if ((mode & 0x10) == 0) {
|
||||||
|
// input
|
||||||
|
gpio_direction |= mask;
|
||||||
|
if (mode == GPIO_Mode_IPU) {
|
||||||
|
gpio_pullup |= mask;
|
||||||
|
} else {
|
||||||
|
gpio_pullup &= ~mask;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// output
|
||||||
|
gpio_direction &= ~mask;
|
||||||
|
gpio_pullup &= ~mask;
|
||||||
|
}
|
||||||
|
|
||||||
|
GPIO_InitTypeDef cfg = {0};
|
||||||
|
cfg.GPIO_Mode = mode;
|
||||||
|
cfg.GPIO_Pin = GPIO_MAP[gpio].pin;
|
||||||
|
cfg.GPIO_Speed = GPIO_Speed_50MHz;
|
||||||
|
GPIO_Init(GPIO_MAP[gpio].port, &cfg);
|
||||||
|
|
||||||
|
gpio_config[gpio] = mode;
|
||||||
|
|
||||||
|
LOG_DEBUG(
|
||||||
|
"GPIO %d - %s %s" EOL,
|
||||||
|
gpio,
|
||||||
|
((gpio_direction & mask) != 0 ? "In" : "Out"),
|
||||||
|
((gpio_direction & mask) != 0 ?
|
||||||
|
(mode == GPIO_Mode_AIN ?
|
||||||
|
"ADC"
|
||||||
|
:
|
||||||
|
((gpio_pullup & mask) != 0 ? "PU" : "PD")
|
||||||
|
)
|
||||||
|
:
|
||||||
|
"PP"
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
void MOD_IO2::GPIOSet(uint8_t gpio, BitAction level) {
|
||||||
|
GPIO_WriteBit(GPIO_MAP[gpio].port, GPIO_MAP[gpio].pin, level);
|
||||||
|
if (level) {
|
||||||
|
gpio_state |= (1 << gpio);
|
||||||
|
} else {
|
||||||
|
gpio_state &= ~(1 << gpio);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t MOD_IO2::GPIOGet(uint8_t gpio) {
|
||||||
|
uint8_t level;
|
||||||
|
|
||||||
|
if (((uint8_t)gpio_config[gpio] & 0x10) == 0) {
|
||||||
|
level = GPIO_ReadInputDataBit(GPIO_MAP[gpio].port, GPIO_MAP[gpio].pin);
|
||||||
|
} else {
|
||||||
|
level = GPIO_ReadOutputDataBit(GPIO_MAP[gpio].port, GPIO_MAP[gpio].pin);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (level) {
|
||||||
|
gpio_state |= (1 << gpio);
|
||||||
|
} else {
|
||||||
|
gpio_state &= ~(1 << gpio);
|
||||||
|
}
|
||||||
|
|
||||||
|
return level;
|
||||||
|
}
|
||||||
|
|
||||||
|
void MOD_IO2::RelayConfig(uint8_t relay) {
|
||||||
|
LOG_DEBUG("RelayConfig(%d)" EOL, relay);
|
||||||
|
if (relay >= MOD_IO2_RELAY_COUNT) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
GPIO_InitTypeDef cfg = {0};
|
||||||
|
|
||||||
|
cfg.GPIO_Mode = GPIO_Mode_Out_PP;
|
||||||
|
cfg.GPIO_Speed = GPIO_Speed_50MHz;
|
||||||
|
cfg.GPIO_Pin = RELAY_MAP[relay].pin;
|
||||||
|
|
||||||
|
GPIO_Init(RELAY_MAP[relay].port, &cfg);
|
||||||
|
}
|
||||||
|
|
||||||
|
void MOD_IO2::RelaySet(uint8_t relay, BitAction level) {
|
||||||
|
GPIO_WriteBit(RELAY_MAP[relay].port, RELAY_MAP[relay].pin, level);
|
||||||
|
if (level) {
|
||||||
|
relay_state |= (1 << relay);
|
||||||
|
} else {
|
||||||
|
relay_state &= ~(1 << relay);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void MOD_IO2::PWMDisable(TIM_TypeDef *TIM) {
|
||||||
|
TIM_Cmd(TIM, DISABLE);
|
||||||
|
TIM_CtrlPWMOutputs(TIM, DISABLE);
|
||||||
|
}
|
||||||
|
|
||||||
|
void MOD_IO2::PWMConfig(TIM_TypeDef *TIM, uint8_t channel, uint16_t pulse) {
|
||||||
|
TIM_Cmd(TIM, DISABLE);
|
||||||
|
TIM_CtrlPWMOutputs(TIM, DISABLE);
|
||||||
|
|
||||||
|
TIM_TimeBaseInitTypeDef TimerConfig;
|
||||||
|
TimerConfig.TIM_Period = 255;
|
||||||
|
TimerConfig.TIM_Prescaler = 608;
|
||||||
|
TimerConfig.TIM_CounterMode = TIM_CounterMode_Up;
|
||||||
|
TimerConfig.TIM_ClockDivision = TIM_CKD_DIV1;
|
||||||
|
TIM_TimeBaseInit(TIM, &TimerConfig);
|
||||||
|
|
||||||
|
TIM_OCInitTypeDef TIM_OCConfig={0};
|
||||||
|
TIM_OCConfig.TIM_Pulse = pulse;
|
||||||
|
TIM_OCConfig.TIM_OCMode = TIM_OCMode_PWM1;
|
||||||
|
TIM_OCConfig.TIM_OutputState = TIM_OutputState_Enable;
|
||||||
|
TIM_OCConfig.TIM_OCPolarity = TIM_OCPolarity_High;
|
||||||
|
|
||||||
|
switch (channel) {
|
||||||
|
case 1:
|
||||||
|
TIM_OC1Init(TIM, &TIM_OCConfig );
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
TIM_OC2Init(TIM, &TIM_OCConfig );
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
TIM_OC3Init(TIM, &TIM_OCConfig );
|
||||||
|
break;
|
||||||
|
case 4:
|
||||||
|
TIM_OC4Init(TIM, &TIM_OCConfig );
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
TIM_CtrlPWMOutputs(TIM, ENABLE);
|
||||||
|
|
||||||
|
switch (channel) {
|
||||||
|
case 1:
|
||||||
|
TIM_OC1PreloadConfig(TIM, TIM_OCPreload_Disable);
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
TIM_OC2PreloadConfig(TIM, TIM_OCPreload_Disable);
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
TIM_OC3PreloadConfig(TIM, TIM_OCPreload_Disable);
|
||||||
|
break;
|
||||||
|
case 4:
|
||||||
|
TIM_OC4PreloadConfig(TIM, TIM_OCPreload_Disable);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
TIM_ARRPreloadConfig(TIM, ENABLE);
|
||||||
|
|
||||||
|
TIM_Cmd(TIM, ENABLE);
|
||||||
|
}
|
208
red/mod-io2/src/mod_io2.h
Normal file
208
red/mod-io2/src/mod_io2.h
Normal file
|
@ -0,0 +1,208 @@
|
||||||
|
#ifndef __MOD_IO2_H
|
||||||
|
#define __MOD_IO2_H
|
||||||
|
|
||||||
|
#include <ch32v00x.h>
|
||||||
|
|
||||||
|
#include "i2c_slave.h"
|
||||||
|
|
||||||
|
#define MOD_IO2_ADDRESS 0x21
|
||||||
|
|
||||||
|
#define MOD_IO2_BOARD_ID 0x23
|
||||||
|
|
||||||
|
#define MOD_IO2_VER_MAJOR 0x5
|
||||||
|
#define MOD_IO2_VER_MINOR 0x0
|
||||||
|
|
||||||
|
#define MOD_IO2_REG_COUNT 21
|
||||||
|
|
||||||
|
#define MOD_IO2_GPIO_COUNT 7
|
||||||
|
|
||||||
|
#define MOD_IO2_RELAY_COUNT 2
|
||||||
|
#define MOD_IO2_RELAY_MASK 0x03
|
||||||
|
|
||||||
|
#define MOD_IO2_ADC_TIMEOUT 100
|
||||||
|
//#define MOD_IO2_ADC_VOLTAGE
|
||||||
|
|
||||||
|
#define MOD_IO2_PWM_COUNT 3
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
MOD_IO2_SET_DIR = 0x01,
|
||||||
|
MOD_IO2_SET_OUT = 0x02,
|
||||||
|
MOD_IO2_GET_IN = 0x03,
|
||||||
|
MOD_IO2_SET_PULL_UP = 0x04,
|
||||||
|
|
||||||
|
MOD_IO2_GET_ANALOG0 = 0x10,
|
||||||
|
MOD_IO2_GET_ANALOG1 = 0x11,
|
||||||
|
MOD_IO2_GET_ANALOG2 = 0x12,
|
||||||
|
MOD_IO2_GET_ANALOG3 = 0x13,
|
||||||
|
MOD_IO2_GET_ANALOG4 = 0x14,
|
||||||
|
MOD_IO2_GET_ANALOG5 = 0x15,
|
||||||
|
MOD_IO2_GET_ANALOG6 = 0x16,
|
||||||
|
|
||||||
|
MOD_IO2_GET_BOARD_ID = 0x20,
|
||||||
|
MOD_IO2_GET_VERSION = 0x21,
|
||||||
|
|
||||||
|
MOD_IO2_RELAY_STATE = 0x40,
|
||||||
|
MOD_IO2_RELAY_ON = 0x41,
|
||||||
|
MOD_IO2_RELAY_OFF = 0x42,
|
||||||
|
MOD_IO2_RELAY_STATE_GET = 0x43,
|
||||||
|
|
||||||
|
MOD_IO2_SET_PWM_OFF = 0x50,
|
||||||
|
MOD_IO2_SET_PWM1 = 0x51,
|
||||||
|
MOD_IO2_SET_PWM2 = 0x52,
|
||||||
|
|
||||||
|
MOD_IO2_SET_ADDRESS = 0xF0,
|
||||||
|
} MOD_IO2_REG_Type;
|
||||||
|
|
||||||
|
/* MOD-IO2 Reg Mapping */
|
||||||
|
typedef struct {
|
||||||
|
uint8_t reg;
|
||||||
|
i2c_read_callback_t onRead;
|
||||||
|
i2c_write_callback_t onWrite;
|
||||||
|
} MOD_IO2_REG_Map_Type;
|
||||||
|
|
||||||
|
/* MOD-IO2 GPIO Mapping */
|
||||||
|
typedef struct {
|
||||||
|
GPIO_TypeDef* port;
|
||||||
|
uint16_t pin;
|
||||||
|
uint8_t adc;
|
||||||
|
TIM_TypeDef * tim;
|
||||||
|
uint8_t tch;
|
||||||
|
} MOD_IO2_GPIO_Map_Type;
|
||||||
|
|
||||||
|
/* MOD-IO2 PWM Mapping */
|
||||||
|
typedef struct {
|
||||||
|
uint8_t gpio;
|
||||||
|
} MOD_IO2_PWM_Map_Type;
|
||||||
|
|
||||||
|
class MOD_IO2 {
|
||||||
|
protected:
|
||||||
|
inline static uint8_t i2c_address = MOD_IO2_ADDRESS;
|
||||||
|
|
||||||
|
static void onSetDir(uint8_t reg, uint8_t offset, uint8_t data);
|
||||||
|
static uint8_t onGetDir(uint8_t reg, uint8_t offset);
|
||||||
|
|
||||||
|
static void onSetPullUp(uint8_t reg, uint8_t offset, uint8_t data);
|
||||||
|
static uint8_t onGetPullUp(uint8_t reg, uint8_t offset);
|
||||||
|
|
||||||
|
static void onSetLevel(uint8_t reg, uint8_t offset, uint8_t data);
|
||||||
|
static uint8_t onGetLevel(uint8_t reg, uint8_t offset);
|
||||||
|
|
||||||
|
static uint8_t onAnalogGet(uint8_t reg, uint8_t offset);
|
||||||
|
|
||||||
|
static uint8_t onGetBoardID(uint8_t reg, uint8_t offset);
|
||||||
|
static uint8_t onGetVersion(uint8_t reg, uint8_t offset);
|
||||||
|
|
||||||
|
static uint8_t onGetRelayState(uint8_t reg, uint8_t offset);
|
||||||
|
static void onSetRelayState(uint8_t reg, uint8_t offset, uint8_t data);
|
||||||
|
|
||||||
|
static void onRelayOn(uint8_t reg, uint8_t offset, uint8_t data);
|
||||||
|
static void onRelayOff(uint8_t reg, uint8_t offset, uint8_t data);
|
||||||
|
static uint8_t onGetRelayOff(uint8_t reg, uint8_t offset);
|
||||||
|
|
||||||
|
static void onSetPWM(uint8_t reg, uint8_t offset, uint8_t data);
|
||||||
|
|
||||||
|
static void onSetAddress(uint8_t reg, uint8_t offset, uint8_t data);
|
||||||
|
|
||||||
|
inline static const MOD_IO2_REG_Map_Type REG_MAP[MOD_IO2_REG_COUNT] = {
|
||||||
|
{.reg = MOD_IO2_SET_DIR, .onRead = onGetDir, .onWrite = onSetDir},
|
||||||
|
{.reg = MOD_IO2_SET_OUT, .onRead = onGetLevel, .onWrite = onSetLevel},
|
||||||
|
{.reg = MOD_IO2_GET_IN, .onRead = onGetLevel, .onWrite = NULL},
|
||||||
|
{.reg = MOD_IO2_SET_PULL_UP, .onRead = onGetPullUp, .onWrite = onSetPullUp},
|
||||||
|
|
||||||
|
// Analog input
|
||||||
|
{.reg = MOD_IO2_GET_ANALOG0, .onRead = onAnalogGet, .onWrite = NULL},
|
||||||
|
{.reg = MOD_IO2_GET_ANALOG5, .onRead = onAnalogGet, .onWrite = NULL},
|
||||||
|
{.reg = MOD_IO2_GET_ANALOG6, .onRead = onAnalogGet, .onWrite = NULL},
|
||||||
|
|
||||||
|
{.reg = MOD_IO2_GET_BOARD_ID, .onRead = onGetBoardID, .onWrite = NULL},
|
||||||
|
{.reg = MOD_IO2_GET_VERSION, .onRead = onGetVersion, .onWrite = NULL},
|
||||||
|
|
||||||
|
{.reg = MOD_IO2_RELAY_STATE, .onRead = onGetRelayState, .onWrite = onSetRelayState},
|
||||||
|
{.reg = MOD_IO2_RELAY_ON, .onRead = onGetRelayState, .onWrite = onRelayOn},
|
||||||
|
{.reg = MOD_IO2_RELAY_OFF, .onRead = onGetRelayOff, .onWrite = onRelayOff},
|
||||||
|
{.reg = MOD_IO2_RELAY_STATE_GET, .onRead = onGetRelayState, .onWrite = NULL},
|
||||||
|
|
||||||
|
// PWM
|
||||||
|
{.reg = MOD_IO2_SET_PWM_OFF, .onRead = NULL, .onWrite = onSetPWM},
|
||||||
|
{.reg = MOD_IO2_SET_PWM1, .onRead = NULL, .onWrite = onSetPWM},
|
||||||
|
{.reg = MOD_IO2_SET_PWM2, .onRead = NULL, .onWrite = onSetPWM},
|
||||||
|
|
||||||
|
// DAC is NOT available
|
||||||
|
{.reg = 0x60, .onRead = NULL, .onWrite = NULL},
|
||||||
|
|
||||||
|
{.reg = MOD_IO2_SET_ADDRESS, .onRead = NULL, .onWrite = onSetAddress},
|
||||||
|
};
|
||||||
|
|
||||||
|
inline static const MOD_IO2_GPIO_Map_Type PGM1_JUMPER = {
|
||||||
|
.port = GPIOD, .pin = GPIO_Pin_1, .adc = 255, .tim = NULL, .tch = 255
|
||||||
|
};
|
||||||
|
|
||||||
|
// GPIO map
|
||||||
|
inline static const MOD_IO2_GPIO_Map_Type GPIO_MAP[MOD_IO2_GPIO_COUNT] = {
|
||||||
|
{.port = GPIOC, .pin = GPIO_Pin_4, .adc = 2, .tim = NULL, .tch = 255}, // GPIO0 - PC4
|
||||||
|
{.port = GPIOC, .pin = GPIO_Pin_5, .adc = 255, .tim = NULL, .tch = 255}, // GPIO1 - PC5
|
||||||
|
{.port = GPIOC, .pin = GPIO_Pin_6, .adc = 255, .tim = NULL, .tch = 255}, // GPIO2 - PC6
|
||||||
|
{.port = GPIOC, .pin = GPIO_Pin_7, .adc = 255, .tim = NULL, .tch = 255}, // GPIO3 - PC7
|
||||||
|
{.port = GPIOC, .pin = GPIO_Pin_0, .adc = 255, .tim = NULL, .tch = 255}, // GPIO4 - PC0
|
||||||
|
{.port = GPIOD, .pin = GPIO_Pin_3, .adc = 4, .tim = TIM2, .tch = 2}, // GPIO5 - PD3 - A4 - T2CH2
|
||||||
|
{.port = GPIOD, .pin = GPIO_Pin_2, .adc = 3, .tim = TIM1, .tch = 1}, // GPIO6 - PD2 - A3 - T1CH1
|
||||||
|
};
|
||||||
|
|
||||||
|
// GPIO cofiguration
|
||||||
|
inline static GPIOMode_TypeDef gpio_config[MOD_IO2_GPIO_COUNT] = {
|
||||||
|
GPIO_Mode_IN_FLOATING, // GPIO0
|
||||||
|
GPIO_Mode_IN_FLOATING, // GPIO1
|
||||||
|
GPIO_Mode_IN_FLOATING, // GPIO2
|
||||||
|
GPIO_Mode_IN_FLOATING, // GPIO3
|
||||||
|
GPIO_Mode_IN_FLOATING, // GPIO4
|
||||||
|
GPIO_Mode_IN_FLOATING, // GPIO5
|
||||||
|
GPIO_Mode_IN_FLOATING, // GPIO6
|
||||||
|
};
|
||||||
|
|
||||||
|
// RELAY map
|
||||||
|
inline static const MOD_IO2_GPIO_Map_Type RELAY_MAP[MOD_IO2_RELAY_COUNT] = {
|
||||||
|
{.port = GPIOA, .pin = GPIO_Pin_1}, // RELAY1
|
||||||
|
{.port = GPIOA, .pin = GPIO_Pin_2}, // RELAY2
|
||||||
|
};
|
||||||
|
|
||||||
|
// PWM map
|
||||||
|
inline static const MOD_IO2_PWM_Map_Type PWM_MAP[MOD_IO2_PWM_COUNT] = {
|
||||||
|
{.gpio = 255}, // PWM0
|
||||||
|
{.gpio = 6}, // PWM1
|
||||||
|
{.gpio = 5}, // PWM2
|
||||||
|
};
|
||||||
|
|
||||||
|
// MOD_IO2 state
|
||||||
|
inline static const MOD_IO2_REG_Map_Type* i2c_reg = NULL;
|
||||||
|
|
||||||
|
inline static uint8_t gpio_direction = 0x7F;
|
||||||
|
inline static uint8_t gpio_pullup = 0x00;
|
||||||
|
inline static uint8_t gpio_state = 0x00;
|
||||||
|
|
||||||
|
inline static bool adc_error = false;
|
||||||
|
inline static uint16_t adc_value = 0x0000;
|
||||||
|
inline static uint16_t adc_voltage = 0x0000;
|
||||||
|
|
||||||
|
inline static uint8_t relay_state = 0x00;
|
||||||
|
|
||||||
|
static void Begin();
|
||||||
|
|
||||||
|
static void GPIOConfig(uint8_t gpio, GPIOMode_TypeDef mode, bool initial = false);
|
||||||
|
static void GPIOSet(uint8_t gpio, BitAction level);
|
||||||
|
static uint8_t GPIOGet(uint8_t gpio);
|
||||||
|
|
||||||
|
static void RelayConfig(uint8_t relay);
|
||||||
|
static void RelaySet(uint8_t relay, BitAction level);
|
||||||
|
|
||||||
|
static void PWMDisable(TIM_TypeDef *TIM);
|
||||||
|
static void PWMConfig(TIM_TypeDef *TIM, uint8_t channel, uint16_t pulse);
|
||||||
|
|
||||||
|
public:
|
||||||
|
static void Setup();
|
||||||
|
static void Setup(uint8_t addr);
|
||||||
|
static void onRegisterSet(uint8_t reg);
|
||||||
|
static uint8_t onRegRead(uint8_t reg, uint8_t offset);
|
||||||
|
static void onRegWrite(uint8_t reg, uint8_t offset, uint8_t data);
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif /* __MOD_IO2_H */
|
51
red/mod-io2/src/uptime.c
Normal file
51
red/mod-io2/src/uptime.c
Normal file
|
@ -0,0 +1,51 @@
|
||||||
|
#include "uptime.h"
|
||||||
|
|
||||||
|
static uint32_t uptime_seconds = 0;
|
||||||
|
|
||||||
|
static uint32_t p_ms = 0;
|
||||||
|
static uint32_t p_us = 0;
|
||||||
|
|
||||||
|
void Uptime_Init() {
|
||||||
|
if (p_ms != 0) {
|
||||||
|
// Already initialized
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
p_ms = (uint32_t)(SystemCoreClock / 1000);
|
||||||
|
p_us = (uint32_t)(SystemCoreClock / 1000000);
|
||||||
|
|
||||||
|
// Interrupt every second
|
||||||
|
NVIC_EnableIRQ(SysTicK_IRQn);
|
||||||
|
|
||||||
|
SysTick->SR &= ~(1 << 0);
|
||||||
|
SysTick->CMP = SystemCoreClock-1;
|
||||||
|
SysTick->CNT = 0;
|
||||||
|
SysTick->CTLR = 0xF;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t Uptime_S() {
|
||||||
|
return uptime_seconds;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t Uptime_Ms() {
|
||||||
|
return uptime_seconds * 1000 + (uint32_t)(SysTick->CNT / p_ms);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t Uptime_Us() {
|
||||||
|
return uptime_seconds * 1000000 + (uint32_t)(SysTick->CNT / p_us);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Wait_Ms(uint32_t delay) {
|
||||||
|
uint32_t start = Uptime_Ms();
|
||||||
|
while((Uptime_Ms() - start) < delay);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Wait_Us(uint32_t delay) {
|
||||||
|
uint32_t start = Uptime_Us();
|
||||||
|
while((Uptime_Us() - start) < delay);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SysTick_Handler(void) {
|
||||||
|
uptime_seconds++;
|
||||||
|
SysTick->SR = 0;
|
||||||
|
}
|
29
red/mod-io2/src/uptime.h
Normal file
29
red/mod-io2/src/uptime.h
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
#ifndef __UPTIME_H
|
||||||
|
#define __UPTIME_H
|
||||||
|
|
||||||
|
#include <ch32v00x.h>
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
void Uptime_Init();
|
||||||
|
|
||||||
|
uint32_t Uptime_S();
|
||||||
|
|
||||||
|
uint32_t Uptime_Us();
|
||||||
|
|
||||||
|
uint32_t Uptime_Ms();
|
||||||
|
|
||||||
|
void Wait_Us(uint32_t delay);
|
||||||
|
|
||||||
|
void Wait_Ms(uint32_t delay);
|
||||||
|
|
||||||
|
void SysTick_Handler(void) __attribute__((interrupt("WCH-Interrupt-fast")));
|
||||||
|
void SysTick_Handler(void);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
11
red/mod-io2/test/README
Normal file
11
red/mod-io2/test/README
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
|
||||||
|
This directory is intended for PlatformIO Test Runner 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/en/latest/advanced/unit-testing/index.html
|
Loading…
Reference in a new issue