Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -206,6 +206,11 @@
* Separate ./README on parts for firmware dev and contributor parts
* Corrected library.json – Fix a license type from MIT to Apache
* TODO Rework bits module
* PCA/CCP module implemented (pca.h): pin group selection, counter clock source, 16 bit counter, compare/capture, high speed toggle output and 8/7/6 bit PWM
* PCA examples added to examples/pca (PWM, 16 bit timer and capture)
* Host tests for library headers added to test/ and wired to ctest
* SPI module implemented (spi.h): master and slave mode, 4 clock dividers, all 4 CPOL/CPHA modes, MSB/LSB first, 3 pin groups, sync and async transfer, write collision detection
* SPI example added to examples/spi (master loopback)

Next releases todo

Expand Down
37 changes: 0 additions & 37 deletions CMakeLists.txt

This file was deleted.

5 changes: 5 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,8 @@ void sleep_ms(uint16_t ms) {
| **EEPROM / IAP** | `eeprom.h` | Read byte, write byte/array, and sector erase via IAP registers | Ready |
| **ADC** | `adc.h` | 10-bit analog-to-digital converter (sync blocking and async interrupt modes) | Ready |
| **Comparator** | `comparator.h` | On-chip analog voltage comparator | Initial Support |
| **PCA / CCP** | `pca.h` | 16-bit counter, compare/capture, high-speed toggle output, 8/7/6-bit PWM | Initial Support |
| **SPI** | `spi.h` | Master & slave, 3 pin groups, 4 clock dividers, CPOL/CPHA modes, sync & async | Initial Support |
| **Bit Operations** | `bits.h` | Fast bit set, clear, toggle, and test macros | Ready |

### Code Examples
Expand Down Expand Up @@ -316,9 +318,12 @@ STC15/
│ ├── eeprom/ # EEPROM read, write byte/page, erase examples
│ ├── gpio/ # GPIO pin modes and values examples
│ ├── interrupt/ # External interrupt & priority examples
│ ├── pca/ # PCA/CCP: PWM, 16-bit timer, and capture examples
│ ├── power_management/# Idle, Power-Down, and Wake-up timer examples
│ ├── spi/ # SPI master & slave examples
│ ├── timer/ # Timer0 & Timer2 mode examples (sync & async)
│ └── uart/ # UART1 modes 0, 1, 2, 3 examples
├── test/ # Host tests for the library headers (ctest)
├── utils/ # Diagnostic and data conversion tools (mem_dump, csv2hex)
├── docs/ # Generated Doxygen documentation
├── doxygen.conf # Doxygen documentation configuration
Expand Down
2 changes: 2 additions & 0 deletions examples/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,10 @@ add_subdirectory("eeprom")
add_subdirectory("frequency")
add_subdirectory("interrupt")
add_subdirectory("gpio")
add_subdirectory("pca")
add_subdirectory("power_management")
add_subdirectory("reset")
add_subdirectory("spi")
add_subdirectory("timer")
add_subdirectory("uart")
add_subdirectory("wdt")
Expand Down
4 changes: 4 additions & 0 deletions examples/SConstruct
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,10 @@ eeprom_progs = SConscript('eeprom/SConscript', exports = 'env', variant_dir=
frequency_progs = SConscript('frequency/SConscript', exports = 'env', variant_dir= base_dir + '/frequency')
gpio_progs = SConscript('gpio/SConscript', exports = 'env', variant_dir= base_dir + '/gpio')
interrupt_progs = SConscript('interrupt/SConscript', exports = 'env', variant_dir= base_dir + '/interrupt')
pca_progs = SConscript('pca/SConscript', exports = 'env', variant_dir= base_dir + '/pca')
pwr_mgmt_progs = SConscript('power_management/SConscript', exports = 'env', variant_dir= base_dir + '/power_management')
reset_progs = SConscript('reset/SConscript', exports = 'env', variant_dir= base_dir + '/reset')
spi_progs = SConscript('spi/SConscript', exports = 'env', variant_dir= base_dir + '/spi')
timer_progs = SConscript('timer/SConscript', exports = 'env', variant_dir= base_dir + '/timer')
uart_progs = SConscript('uart/SConscript', exports = 'env', variant_dir= base_dir + '/uart')
wdt_progs = SConscript('wdt/SConscript', exports = 'env', variant_dir= base_dir + '/wdt')
Expand All @@ -41,8 +43,10 @@ Return([
'frequency_progs',
'gpio_progs',
'interrupt_progs',
'pca_progs',
'pwr_mgmt_progs',
'reset_progs',
'spi_progs',
'timer_progs',
'uart_progs',
'wdt_progs',
Expand Down
40 changes: 40 additions & 0 deletions examples/pca/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
add_executable(pca_pwm_example
pca_pwm_example.c
)
add_executable(pca_timer_example
pca_timer_example.c
)
add_executable(pca_capture_example
pca_capture_example.c
)

target_link_libraries(pca_pwm_example PRIVATE stc15hal)
target_link_libraries(pca_timer_example PRIVATE stc15hal)
target_link_libraries(pca_capture_example PRIVATE stc15hal)

# Transform firmware ihx to hex
ihx_to_hex(pca_pwm_example)
ihx_to_hex(pca_timer_example)
ihx_to_hex(pca_capture_example)

# upload firmware to STC MCU
add_custom_target(flash_pca_pwm
COMMAND stcgal ${CMAKE_BINARY_DIR}/examples/pca/pca_pwm_example.hex
DEPENDS pca_pwm_example
COMMENT "Waiting for MCU, please cycle power:"
USES_TERMINAL
)
# upload firmware to STC MCU
add_custom_target(flash_pca_timer
COMMAND stcgal ${CMAKE_BINARY_DIR}/examples/pca/pca_timer_example.hex
DEPENDS pca_timer_example
COMMENT "Waiting for MCU, please cycle power:"
USES_TERMINAL
)
# upload firmware to STC MCU
add_custom_target(flash_pca_capture
COMMAND stcgal ${CMAKE_BINARY_DIR}/examples/pca/pca_capture_example.hex
DEPENDS pca_capture_example
COMMENT "Waiting for MCU, please cycle power:"
USES_TERMINAL
)
28 changes: 28 additions & 0 deletions examples/pca/SConscript
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
Import("env")

pca_pwm_prog = env.Program(
'pca_pwm_example',
['pca_pwm_example.c']
)

pca_timer_prog = env.Program(
'pca_timer_example',
['pca_timer_example.c']
)

pca_capture_prog = env.Program(
'pca_capture_example',
['pca_capture_example.c']
)

env.Default(
pca_pwm_prog,
pca_timer_prog,
pca_capture_prog
)

Return([
'pca_pwm_prog',
'pca_timer_prog',
'pca_capture_prog',
])
77 changes: 77 additions & 0 deletions examples/pca/pca_capture_example.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
/**
* How to measure a pulse width with PCA module 0 in 16 bit capture mode.
*
* The PCA counter is clocked from SYSclk/12, so one counter tick is 12
* system clocks. Module 0 captures the counter value on both edges of the
* CCP0 pin, and the difference between two captures is the pulse width in
* counter ticks.
*
* Pin group 1 is used, so CCP0 is P1.1. The pin is an input here and must
* be configured as input only.
*
* In capture mode CAPPn and/or CAPNn are set and ECOMn is left clear.
* Setting both CAPP0 and CAPN0 captures on every transition, which makes
* the handler measure the high time and the low time alternately.
*/

#include <pca.h>
#include <gpio.h>
#include <interrupt.h>

// CCP0 is P1.1 in pin group 1. The port is passed to the gpio macros as the
// literal P1 because they paste it into a register name (port ## M1).
#define CCP0_PIN 1

// Captured counter value of the previous edge
static uint16_t previous_capture = 0;

// Width of the last pulse in PCA counter ticks
static uint16_t pulse_width = 0;

/*
* PCA interrupt handler.
*
* The CCF0 flag is cleared before the capture registers are read, so a new
* capture cannot be missed while the value is being copied out.
*/
void pca_ISR(void) __interrupt(INTERRUPT_PCA)
{
if (is_pca_module_flag_set(0))
{
pca_clear_module_flag(0);

uint16_t capture = pca_module_get_capture(0);

// Unsigned arithmetic handles the counter wrap around
pulse_width = capture - previous_capture;
previous_capture = capture;
}
}

void main(void)
{
// CCP0 is P1.1 in pin group 1 and is used as an input
pca_set_pin_group(PCA_PIN_GROUP_1);
pin_input_only_init(P1, CCP0_PIN);

// Counter clock is SYSclk/12
pca_set_clock_source(PCA_CLOCK_SYS_12);

// Module 0 captures on both edges of CCP0, ECOMn stays clear
pca_module_enable_capture_positive(0);
pca_module_enable_capture_negative(0);

// CCF0 generates a PCA interrupt
pca_module_enable_interrupt(0);

// Load the counter and start it
pca_set_counter(0);
pca_start();

enable_mcu_interrupts();

while (1)
{
// pulse_width holds the width of the last pulse in counter ticks
}
}
53 changes: 53 additions & 0 deletions examples/pca/pca_pwm_example.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
/**
* How to generate a PWM signal on the CCP0 pin with PCA module 0.
*
* The PCA counter is clocked from SYSclk/12 and module 0 is used as an
* 8 bit PWM. The duty cycle is changed every 100 ms, so a LED connected
* to the CCP0 pin fades up and down.
*
* Pin group 1 is used, so CCP0 is P1.1. The pin must be configured as
* push-pull output, otherwise it cannot drive a LED.
*
* PWM frequency is SYSclk / (12 * 256) for 8 bit resolution. With the
* default 24 MHz clock this is about 7.8 kHz.
*/

#include <pca.h>
#include <gpio.h>
#include <delay.h>

// CCP0 is P1.1 in pin group 1. The port is passed to the gpio macros as the
// literal P1 because they paste it into a register name (port ## M1).
#define CCP0_PIN 1

#define DUTY_STEP 1

void main(void)
{
uint8_t duty = 0;

// CCP0 is P1.1 in pin group 1, must be push-pull to drive a LED
pca_set_pin_group(PCA_PIN_GROUP_1);
pin_push_pull_init(P1, CCP0_PIN);

// Counter clock is SYSclk/12, module 0 is an 8 bit PWM
pca_set_clock_source(PCA_CLOCK_SYS_12);
pca_module_pwm_init(0, PCA_PWM_8BIT);

// Start with a zero duty cycle
pca_module_pwm_set_duty(0, 0);

// Load the counter and start it
pca_set_counter(0);
pca_start();

while (1)
{
// CCAP0H is a reload register, the new duty value is applied on
// the next counter overflow without a glitch on the output
pca_module_pwm_set_duty_on_overflow(0, duty);

duty += DUTY_STEP;
delay_ms(100);
}
}
75 changes: 75 additions & 0 deletions examples/pca/pca_timer_example.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
/**
* How to use PCA module 0 as a 16 bit software timer.
*
* The PCA counter is clocked from SYSclk/12. Module 0 compare value is
* set to 10000, so the CCF0 flag is set every 10000 counter ticks. With
* the default 24 MHz clock this is every 5 ms.
*
* The module interrupt is enabled and the CCF0 flag is cleared in the
* handler, so the ISR runs every 5 ms. A LED on P1.0 is toggled every
* 100 interrupts, which is every 500 ms.
*
* Note that the PCA counter overflow flag CF and the module flags CCF0,
* CCF1 and CCF2 share the single PCA interrupt vector.
*/

#include <pca.h>
#include <interrupt.h>

// LED pin
#define LED_PIN P10

// Compare value, CCF0 is set every 10000 counter ticks
#define COMPARE_VALUE 10000

// Number of interrupts in 500 ms
#define INTERRUPTS_PER_HALF_SECOND 100

/*
* PCA interrupt handler.
*
* The CCF0 flag is set on module 0 compare match and must be cleared by
* software. The counter overflow flag CF is not used here, but if it were
* enabled it would have to be cleared as well.
*/
void pca_ISR(void) __interrupt(INTERRUPT_PCA)
{
static uint8_t counter = 0;

if (is_pca_module_flag_set(0))
{
pca_clear_module_flag(0);

counter++;
if (counter >= INTERRUPTS_PER_HALF_SECOND)
{
counter = 0;
LED_PIN = !LED_PIN;
}
}
}

void main(void)
{
// Counter clock is SYSclk/12
pca_set_clock_source(PCA_CLOCK_SYS_12);

// Module 0 compares the counter with COMPARE_VALUE
pca_module_set_compare(0, COMPARE_VALUE);
pca_module_enable_comparator(0);
pca_module_enable_match(0);

// CCF0 generates a PCA interrupt
pca_module_enable_interrupt(0);

// Load the counter and start it
pca_set_counter(0);
pca_start();

enable_mcu_interrupts();

while (1)
{
// All the work is done in pca_ISR
}
}
Loading