aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorUri Shaked2021-07-15 17:53:05 +0300
committerUri Shaked2021-07-15 17:53:05 +0300
commit77afa2308ed969a9583149646d05e26716f672d8 (patch)
tree221a129adee9da672660ac6fe8c05a37f33358e8 /src
parent0.16.1 (diff)
downloadavr8js-77afa2308ed969a9583149646d05e26716f672d8.tar.gz
avr8js-77afa2308ed969a9583149646d05e26716f672d8.tar.bz2
avr8js-77afa2308ed969a9583149646d05e26716f672d8.zip
feat(usart): add configuration change event
also add `txEnable` and `rxEnable` properties
Diffstat (limited to 'src')
-rw-r--r--src/peripherals/usart.spec.ts56
-rw-r--r--src/peripherals/usart.ts42
2 files changed, 95 insertions, 3 deletions
diff --git a/src/peripherals/usart.spec.ts b/src/peripherals/usart.spec.ts
index f92ea00..8dbd5ce 100644
--- a/src/peripherals/usart.spec.ts
+++ b/src/peripherals/usart.spec.ts
@@ -53,6 +53,24 @@ describe('USART', () => {
expect(usart.baudRate).toEqual(2400);
});
+ it('should call onConfigurationChange when the baudRate changes', () => {
+ const cpu = new CPU(new Uint16Array(1024));
+ const usart = new AVRUSART(cpu, usart0Config, FREQ_16MHZ);
+ const onConfigurationChange = jest.fn();
+ usart.onConfigurationChange = onConfigurationChange;
+
+ cpu.writeData(UBRR0H, 0);
+ expect(onConfigurationChange).toHaveBeenCalled();
+
+ onConfigurationChange.mockClear();
+ cpu.writeData(UBRR0L, 5);
+ expect(onConfigurationChange).toHaveBeenCalled();
+
+ onConfigurationChange.mockClear();
+ cpu.writeData(UCSR0A, U2X0);
+ expect(onConfigurationChange).toHaveBeenCalled();
+ });
+
describe('bitsPerChar', () => {
it('should return 5-bits per byte when UCSZ = 0', () => {
const cpu = new CPU(new Uint16Array(1024));
@@ -89,6 +107,24 @@ describe('USART', () => {
cpu.writeData(UCSR0B, UCSZ2);
expect(usart.bitsPerChar).toEqual(9);
});
+
+ it('should call onConfigurationChange when bitsPerChar change', () => {
+ const cpu = new CPU(new Uint16Array(1024));
+ const usart = new AVRUSART(cpu, usart0Config, FREQ_16MHZ);
+ const onConfigurationChange = jest.fn();
+ usart.onConfigurationChange = onConfigurationChange;
+
+ cpu.writeData(UCSR0C, UCSZ0 | UCSZ1);
+ expect(onConfigurationChange).toHaveBeenCalled();
+
+ onConfigurationChange.mockClear();
+ cpu.writeData(UCSR0B, UCSZ2);
+ expect(onConfigurationChange).toHaveBeenCalled();
+
+ onConfigurationChange.mockClear();
+ cpu.writeData(UCSR0B, UCSZ2);
+ expect(onConfigurationChange).not.toHaveBeenCalled();
+ });
});
describe('stopBits', () => {
@@ -145,6 +181,26 @@ describe('USART', () => {
expect(usart.onByteTransmit).toHaveBeenCalledWith(0x61);
});
+ describe('txEnable/rxEnable', () => {
+ it('txEnable should equal true when the transitter is enabled', () => {
+ const cpu = new CPU(new Uint16Array(1024));
+ const usart = new AVRUSART(cpu, usart0Config, FREQ_16MHZ);
+ usart.onByteTransmit = jest.fn();
+ expect(usart.txEnable).toEqual(false);
+ cpu.writeData(UCSR0B, TXEN);
+ expect(usart.txEnable).toEqual(true);
+ });
+
+ it('rxEnable should equal true when the transitter is enabled', () => {
+ const cpu = new CPU(new Uint16Array(1024));
+ const usart = new AVRUSART(cpu, usart0Config, FREQ_16MHZ);
+ usart.onByteTransmit = jest.fn();
+ expect(usart.rxEnable).toEqual(false);
+ cpu.writeData(UCSR0B, RXEN);
+ expect(usart.rxEnable).toEqual(true);
+ });
+ });
+
describe('tick()', () => {
it('should trigger data register empty interrupt if UDRE is set', () => {
const cpu = new CPU(new Uint16Array(1024));
diff --git a/src/peripherals/usart.ts b/src/peripherals/usart.ts
index f55fef3..3ad787d 100644
--- a/src/peripherals/usart.ts
+++ b/src/peripherals/usart.ts
@@ -3,7 +3,7 @@
* Part of AVR8js
* Reference: http://ww1.microchip.com/downloads/en/DeviceDoc/ATmega48A-PA-88A-PA-168A-PA-328-P-DS-DS40002061A.pdf
*
- * Copyright (C) 2019, 2020, Uri Shaked
+ * Copyright (C) 2019, 2020, 2021 Uri Shaked
*/
import { AVRInterruptConfig, CPU } from '../cpu/cpu';
@@ -36,6 +36,7 @@ export const usart0Config: USARTConfig = {
export type USARTTransmitCallback = (value: u8) => void;
export type USARTLineTransmitCallback = (value: string) => void;
+export type USARTConfigurationChangeCallback = () => void;
/* eslint-disable @typescript-eslint/no-unused-vars */
// Register bits:
@@ -47,6 +48,7 @@ const UCSRA_DOR = 0x8; // Data OverRun
const UCSRA_UPE = 0x4; // USART Parity Error
const UCSRA_U2X = 0x2; // Double the USART Transmission Speed
const UCSRA_MPCM = 0x1; // Multi-processor Communication Mode
+const UCSRA_CFG_MASK = UCSRA_U2X;
const UCSRB_RXCIE = 0x80; // RX Complete Interrupt Enable
const UCSRB_TXCIE = 0x40; // TX Complete Interrupt Enable
const UCSRB_UDRIE = 0x20; // USART Data Register Empty Interrupt Enable
@@ -55,6 +57,7 @@ const UCSRB_TXEN = 0x8; // Transmitter Enable
const UCSRB_UCSZ2 = 0x4; // Character Size 2
const UCSRB_RXB8 = 0x2; // Receive Data Bit 8
const UCSRB_TXB8 = 0x1; // Transmit Data Bit 8
+const UCSRB_CFG_MASK = UCSRB_UCSZ2 | UCSRB_RXEN | UCSRB_TXEN;
const UCSRC_UMSEL1 = 0x80; // USART Mode Select 1
const UCSRC_UMSEL0 = 0x40; // USART Mode Select 0
const UCSRC_UPM1 = 0x20; // Parity Mode 1
@@ -76,6 +79,7 @@ export class AVRUSART {
public onByteTransmit: USARTTransmitCallback | null = null;
public onLineTransmit: USARTLineTransmitCallback | null = null;
public onRxComplete: (() => void) | null = null;
+ public onConfigurationChange: USARTConfigurationChangeCallback | null = null;
private rxBusyValue = false;
private rxByte = 0;
@@ -107,9 +111,12 @@ export class AVRUSART {
constructor(private cpu: CPU, private config: USARTConfig, private freqHz: number) {
this.reset();
- this.cpu.writeHooks[config.UCSRA] = (value) => {
+ this.cpu.writeHooks[config.UCSRA] = (value, oldValue) => {
cpu.data[config.UCSRA] = value & (UCSRA_MPCM | UCSRA_U2X);
cpu.clearInterruptByFlag(this.TXC, value);
+ if ((value & UCSRA_CFG_MASK) !== (oldValue & UCSRA_CFG_MASK)) {
+ this.onConfigurationChange?.();
+ }
return true;
};
this.cpu.writeHooks[config.UCSRB] = (value, oldValue) => {
@@ -123,6 +130,16 @@ export class AVRUSART {
// Enabling the transmission - mark UDR as empty
cpu.setInterruptFlag(this.UDRE);
}
+ cpu.data[config.UCSRB] = value;
+ if ((value & UCSRB_CFG_MASK) !== (oldValue & UCSRB_CFG_MASK)) {
+ this.onConfigurationChange?.();
+ }
+ return true;
+ };
+ this.cpu.writeHooks[config.UCSRC] = (value) => {
+ cpu.data[config.UCSRC] = value;
+ this.onConfigurationChange?.();
+ return true;
};
this.cpu.readHooks[config.UDR] = () => {
const mask = rxMasks[this.bitsPerChar] ?? 0xff;
@@ -151,6 +168,16 @@ export class AVRUSART {
this.cpu.clearInterrupt(this.TXC);
this.cpu.clearInterrupt(this.UDRE);
};
+ this.cpu.writeHooks[config.UBRRH] = (value) => {
+ this.cpu.data[config.UBRRH] = value;
+ this.onConfigurationChange?.();
+ return true;
+ };
+ this.cpu.writeHooks[config.UBRRL] = (value) => {
+ this.cpu.data[config.UBRRL] = value;
+ this.onConfigurationChange?.();
+ return true;
+ };
}
reset() {
@@ -187,13 +214,22 @@ export class AVRUSART {
}
private get UBRR() {
- return (this.cpu.data[this.config.UBRRH] << 8) | this.cpu.data[this.config.UBRRL];
+ const { UBRRH, UBRRL } = this.config;
+ return (this.cpu.data[UBRRH] << 8) | this.cpu.data[UBRRL];
}
private get multiplier() {
return this.cpu.data[this.config.UCSRA] & UCSRA_U2X ? 8 : 16;
}
+ get rxEnable() {
+ return !!(this.cpu.data[this.config.UCSRB] & UCSRB_RXEN);
+ }
+
+ get txEnable() {
+ return !!(this.cpu.data[this.config.UCSRB] & UCSRB_TXEN);
+ }
+
get baudRate() {
return Math.floor(this.freqHz / (this.multiplier * (1 + this.UBRR)));
}