aboutsummaryrefslogtreecommitdiff
path: root/src/peripherals
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/peripherals
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 '')
-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)));
}
If you find this content useful please consider a small donation via bitcoin: bitcoin:1mxKyQsHHugqRxPKgwaA7wUwJEGCNthn2?amount=0.001&message=Donation Scraping git contents via cgit is very resource intensive. The continued hosting of these git repositories is entirely financed by your bitcoin contributions. For donations of a least 1 mBTC you include your IP address in the message of your bitcoin donation. This will remove this message in the future for requests from your IP, which will significantly reduce token usage. In any case, any contributions to bitcoin address 1mxKyQsHHugqRxPKgwaA7wUwJEGCNthn2 are very welcome. Thanks in advance for you contribution to a self-sustaining ecosystem.