Closed ricardojlrufino closed 2 years ago
Its a PULLUP problem... this code must print:
[up] 1
[down] 1
void setup() {
Serial.begin(115200);
pinMode(2, INPUT_PULLUP);
digitalWrite(2, HIGH);
pinMode(3, INPUT_PULLUP);
digitalWrite(3, HIGH);
pinMode(13, OUTPUT);
digitalWrite(13, HIGH);
delay(1000); // Pause for 2 seconds
Serial.print("[up] \n"); Serial.println(digitalRead(3));
Serial.print("[down] \n"); Serial.println(digitalRead(2));
delay(1000); // Pause for 2 seconds
}
void loop() {
int up = digitalRead(3);
int down = digitalRead(2);
if (up == LOW) {;
Serial.println("[up]");
delay(200);
};
if (down == LOW) {
Serial.println("[down]");
delay(200);
};
}
this implicit PULLUP has same problem If the pin is configured as an INPUT, digitalWrite() will enable (HIGH) or disable (LOW) the internal pullup on the input pin.
pinMode(2, INPUT);
digitalWrite(2, HIGH);
pinMode(3, INPUT);
digitalWrite(3, HIGH);
Working online
AVR8js does not include the logic to deal with pull-up resistors, so the implementation is up to the user of the library.
You can add a listener to the relevant GPIO port (by calling the addListener()
method of the AVRIOPort
class), and then check the state for each of the pins, and call setPin()
to set the final input value of the pin. Something along the following lines:
for (let pin = 0; pin < 8; pin++) {
if (port.pinState(pin) === PinState.InputPullUp) {
// Now, if the button is pressed call port.setPin(pin, false);
// Otherwise call port.setPin(pin, true);
}
}
You can find a more complete example in the AVR8js Simon Game Sample. The code there assumes there's always a pull-up resistor on the pin (e.g. an external pull-up resistor), so the logic is hard coded:
for (const button of buttons) {
const pinIndex = parseInt(button.getAttribute('pin'), 10);
button.addEventListener('button-press', () => {
if (runner) {
runner.portD.setPin(pinIndex, false);
}
});
button.addEventListener('button-release', () => {
if (runner) {
runner.portD.setPin(pinIndex, true);
}
});
}
If you need additional help with this, feel free to reopen / add new comments to this thread
Hi @urish , thanks for the feedback
But this is a feature of the AVR at least 328p, according to the datasheet.
I don't think it would be interesting to include this functionality ?
I rum this code in SimulIDE and PicSimLAB (simavr) and both implement this control...
void setup() {
Serial.begin(115200);
pinMode(2, INPUT);
pinMode(3, INPUT);
digitalWrite(2, HIGH);
digitalWrite(3, HIGH);
delay(100);
Serial.print("[up]:"); Serial.println(digitalRead(3));
Serial.print("[down]:"); Serial.println(digitalRead(2));
delay(1000);
}
void loop() {
}
Online works....
But if i comment, in this example: https://stackblitz.com/edit/avr8js-simon-game-j4rxnx?file=index.ts , not works
for (const button of buttons) {
const pinIndex = parseInt(button.getAttribute('pin'), 10);
//runner.portD.setPin(pinIndex, true);
}
Thanks for the feedback, Ricardo!
It is a design choice to expose the building blocks to the users of the library and let them implement the pin functionality as they wish. AVR8js does let you know if the pull-up for a specific has been enabled or not, but it's up to you to decide how to act upon it. This way, we don't impose any limitations on what you can connect to the pins.
For instance, on Wokwi.com, the implementation takes into account external resistors as well, so you can also use an external pull-up or pull-down resistor. Also, a floating INPUT pin currently has a random state.
Other implementations can choose to also take the resistor values into account (and this might be the case in Wokwi once wokwi/wokwi-features#203 is done), so by adding a 1k resistor between a pulled-up pin and the ground, you'll get the pin to read 0, but adding a 1m resistor the pin will still read 1. For even more accuracy, the implementation can take the chip's internal temperature into account:
One may even integrate AVR8js with a complete SPICE simulation to create a more accurate model of the digital pins, adjusting the pin threshold voltage according to the supply voltage:
I understand, I see that this logic is outside the CPU, my head is still a closed view that the CPU is just one thing...
So deciding how to implement this would be part of controlling peripherals. Something like it's right here?
In my case I "depend" that there is a button, to simulate the pulllup resistor. at wokwi.com, you don't have to. How did you do it there?
It's just that I still think that other people possibly people are going to have this problem, and maybe something like a flag in AVRIOPort , can help toggle this behavior on and off.
Exactly!
Just like the SPI/TWI don't implement the on-wire protocol - they give you some handy callbacks, and you can decide if/how you support these protocols.
In Wokwi, the implementation looks at which devices are connected to each MCU pin. Then, whenever the state of any of these devices changes, it checks whether any of the devices have HIGH or LOW output. If there are none, it checks whether there are any pull up or pull down resistors (internal to the MCU or external). If none are found, it uses a random value (high or low). Once it determines the final digital value of the pin, it calls the setPin()
method with this value.
If Wokwi had an analog simulation engine (similar to falstad), we'd probably model the pull-up resistor using a combination of a 47kΩ resistor (or similar value) + MOSFET that connects/disconnects the resistor according to the MCU pin configuration.
What would the suggested flag in AVRIOPort do? How do you imagine it would work?
I don't know what happens, but online it works, without adding any external components Just Open : https://wokwi.com/projects/new/arduino-uno And run:
void setup() {
Serial.begin(115200);
pinMode(2, INPUT);
digitalWrite(2, HIGH);
delay(100);
if(digitalRead(2) == LOW) Serial.println("FAIL");
else Serial.println("SUCCESS");
}
void loop() {
}
THIS PRINTS: SUCCESS !!
Now on : /avr8js/demo (http://localhost:1234)
This prints : FAIL
--
I believe the right behavior is: IF DDR is set to INPUT, digitalWrite on PORTD/B must update PIND/B
Test using PORTs have sabe result
void setup() {
Serial.begin(115200);
DDRD = 0;
PORTD |= _BV(PD2);
if(PIND & (1 << PD2)) { // if pin 2 high
Serial.print("SUCCESS");
}else{
Serial.print("FAIL");
}
}
void loop() {
}
On SIMAVR, PIND changes !!
https://github.com/buserror/simavr/blob/master/simavr/sim/avr_ioport.c#L78
For the previous print you can understand that it was changed on that place, but no, it was here:
PIND changes here
And here it is changed
The great reason for existing this pull_up, is to do with digitalwrite (P, high), always keep high, maintaining consistency. And the strategy of implementing this in hardware is to put the pull_up ...
But I think I understood the problem in the simulation. Is that if I set a high, but have a pressed button forcing Low. The final state should be "low" by the smallest "resistance". So only the external software can know if it exists or not "something connected";
On the other hand it can reverse logic: When you set the pin for high If in external software does not have anything connected (button): keep high (in case even if you do not have listeners in the port) If in the external software has something connected on the pin, it decides whether it gets low (or high) based on some resistance rule.
Thinkercad ignore internal pull_up
TEST SUMARY, return in: arduino físco: SUCCESS wokwi: SUCCESS AVRSIM / PicSimlab (C): SUCCESS Avr8JS: FAIL Thinkercad: FAIL
I tried, implement a test for this case. But I do not know how to fulfill the fix ... kkkk I'm still learning about AVR architecture
it('should enable internal pull-up if set HIGH to INPUT pin (issue #XXXX)', () => {
const { program } = asmProgram(`
; register addresses
_REPLACE DDRD, ${DDRD - 0x20}
_REPLACE PIND, ${PIND - 0x20}
_REPLACE PORTD, ${PORTD - 0x20}
; Setup
ldi r23, 0x0
out DDRD, r23
; Now toggle pin 2 with SBI
sbi PORTD, 2
break
`);
const cpu = new CPU(program);
const portD = new AVRIOPort(cpu, portDConfig);
const runner = new TestProgramRunner(cpu);
const listener = jest.fn();
portD.addListener(listener);
// Setup: pins 2 as INPUT
runner.runInstructions(2);
expect(cpu.data[PIND]).toEqual(0x0);
// Set: pins 2 as HIGH
runner.runInstructions(1);
expect(listener).toHaveBeenCalledWith(0x04, 0x0);
expect(cpu.data[PORTD]).toEqual(0x4);
expect(cpu.data[PIND]).toEqual(0x4);
});
Thinkercad ignore internal pull_up
This is surprising
But I do not know how to fulfill the fix ... kkkk
I think we can come up with an helper function or class that will attach to the AVRIOPort. That way, people who want to bring their own implementation (like Wokwi does), will keep using the existing API, but if someone just needs a basic implementation with support for buttons, they can use the helper function or class. Thoughts?
I thought about putting a flag on AvrportConfig or AVRIOPort
where the user could decide whether or not to activate this functionality, so would not change the current behavior.
And Something like
Pullup view:
Hi, i'm running some examples of interrupt and I found strange behavior.
Based on the example (which works correctly in wokwi online) https://wokwi.com/projects/318885209198035539
If I run the same example in Avr8js it will trigger the interrupt (e not call loop()) As the inputs are configured as PULLUP, was this supposed to happen?![image](https://user-images.githubusercontent.com/515675/162657179-7a0ac3c4-73e5-4380-9ad8-860ce63a367c.png)
I tried simulating the same behavior, removing the buttons from the example, and it still works correctly https://wokwi.com/projects/328609280331612756
But no work in avr8js-electron-playground , and avr8js demo in my code (which is based on his..) I wonder if I need to configure something else.
Reproduce: