Sucess using your ASM as the source and then adapting to GCBASIC.
This is a user program. This shows using READAD() and READAD10(). These are the GCBASIC public functions to read the ADC.
The functions take a single parameter = AINn where AINn is valid specific to the chip in use.
Working example follows.
#chip mega4809, 5
#option explicit
#DEFINE USART3_BAUD_RATE 9600
#DEFINE USART3_TX_BLOCKING
#DEFINE USART3_DELAY OFF
wait 100 ms
HSerPrintStringCRLF "ADC Read"
Do
HSerPrint ReadAD10 ( AIN1 )
wait 100 ms
Loop
Support with A-D.h ( this lowlevel library) is this. I have documented for those who come this way in the future.
But, the highlights.
The function returns a variable called ReadAD10AVRDx, which is aliased to ADC0_RESH, ADC0_RESL. Therefore, no return value is actually required in the function. The function name is the alias.
A variable called PORTx_PinyCTRL is used as the indirect address to the AINx being sampled/controlled.
To resolve the user and the library havign to support a huge number AINx/PORTx_PinyCTRL mappings a lookup table contains the control register for AINx. This table is read to obtain the correct register. These registers vary across different AVRDx chips, so there are several tables to support all the different AINx to Port_Pin mappings. The table to be used is held in the DAT file in a constant called CHIPADCPPORTMAP. The lookup table handles all mapping between AINx and PORT.PIN, resolving the many chip variants.
The function caches the PORTx_PinyCTRL control register and restores the state upon exiting. This uses indirect addressing, so the user does not need to worry about the function’s impact on the pin control.
The MUX control is simple. However, some chips do not have all AINx constants. For example, AIN0 is missing from some chips. The DAT file now contains the constants AIN0-AIN22. If an AINn is not valid, the constant will not exist.
Users can override the ADC prescaler using #DEFINE AVRX_ADC_PRESC_DIV 4|16. There is syntax checking for 4 or 16 as the parameter in a script.
The crutial thing in all this. Is the user does not have to be concerned is AINn is PORTx_PinyCTRL, as this is actually very different across the range of AVRDx chips and would be very hard to navigate across chips. The table approach make use very simple.
Result on terminal
This is a full swing of the Pot - seems to work great. The two values are the 8bit and 10bit ADC.
I have uploaded the a-d.h, DAT files and a tweaked ( not for AVRDX ) usart.h This needs to go onto of the GSTUDIO build from yesterday. This may need you to change back to mainstream to get the update.
And, I was sent a message to optimise the new a-d.h, so, done.
Optional control constants
Adding additional constants to the user program can save up to 61 words but the functions are constrained to a specific PORTx_Piny
Set explicit port.pin - this will prevent any table lookup - would be used when either a single ADC is used, or, an error in the lookup table
* Saves 22 words but makes the READAD and READAD10 locked on the specified PORTx_Piny
* ANIx MUST be correct for the specified PORTx_Piny
//~ for AIN1 on a MEGA4809#DEFINEAVRX_ADC_PORTx_PinyCTRLPORTD_Pin1CTRL
Do not cache the port.pin setting. Therefore, user needs to manage the PORTx_PinyCTRL state.
* Saves 9 words
#DEFINE AVRX_ADC_NOCACHE_PORTx_PinyCTRL
Change the ADC frequency. No program size impact. This just overrides the default value
#DEFINE AVRX_ADC_PRESC_DIV 16 // Options are 16 or 4
-----------------------
So, with these new constants applied the method looks like this - this would be fixed to a single AINx with no caching.
I have started with fixed mode PWM. Historically, GCBASIC has supported a 38Hz, 50% duty on the first PWM port.pin. I have updated the PWM.h library to support this. Looks good.
The fixed mode PWM signal always generates a PWM signal as follows:
PWM Frequency 38Hz
PWM Duty 50%
Always on the first PWM channel
Directives are
#DEFINE PWM_FREQ 38 // this is the default
#DEFINE PWM_DUTY 50 // this is the default
Controls are
PWMOn
PWMOff
This program is for the mega4809
The PWM signal is generaed PORTA.0
PORTA.0 is the TCA0/WO0_Channel 0 output port.pin
The output port may be different for other AVRDx chips.
Developers:
The supporting PWM script exposes - these can used in programs
SCRIPT_PWM_PERIOD
SCRIPT_PWM_DUTY
SCRIPT_PWMPRESCALER
A program looks like this.
~~~
#chip mega4809
option Explicit
#DEFINE PWM_FREQ 38
#DEFINE PWM_DUTY 50
// Set the PWM port.pin as an Output
// PORTA.0 is the TCA0/WO0_Channel 0 output port.pin
DIR PORTA.0 Out
Do
PWMon
Wait 2 s
// PWMoff
Wait 2 s
Loop
~~~
The ASM is very simple. A script does the heaving lifting to calculate the register values and the prescaler etc. I have attached the ASM.
I will post updated pwm.h when I a few more capabilities added. :-)
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
And, adaption of the basic fixed mode. This changes the duty cycle and shows how to combine the ReadAD() and the Scale() function to ensure the duty cycle matches the frequency.
At the current moment I can point at a certain address and read 32 bytes(less or more) through polling the interrupt flags with my 24LC64 EEPROM. Currently I have turned my TINY1616 into the Master and working on code for M4809 Slave R/W. Error checking will be the last thing implemented. Many facets to the protocol and I'm not strong on it. There seems to be many possible ways to set up interrupts.
I have no other I2C devices to test with so I'm working closely with the Data Sheets and legacy I2C libraries and example code for reference (in "C") as well as the interrupt routine I wrote earlier this year. The app note cites the " Atmel Start " application configuration software for Microchip Studio 7... which is convoluted and useless for me.
Progress?... Yes. Speed?... Slow, Slow, slow.
Small victories to win , I guess.
Glad to hear of your progress!
G
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
This code sets(writes) an address of 32 in the 24LC64, reads the 32 bytes starting @ address 32, prints to USART3 , Waits 2 sec, Repeat. All done by polling the TWI flags.
There is excess code that needs to be trimmed in the " rdone_check: " loop. The program was malfunctioning on my tn1616. I will send updated code later after I verify that the new code works(better)on the m4809. After that, on to the Slave...
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
I have made progress now. I have the functions ready: TWI0Start, TWI0Send, TWI0Stop and TWI0Receive. They are working.
My test program is the same programs as I used for the Software TWI(I2C). They are:
1. TWI Device discovery
2. TWI GLCD
So, this will test all the functions.
Issues
I have one issue test issue to resolve - a silly error that I have not yet resolved is when TWI Device discovery discoveries a device it then thinks every device is present. Must be a bus fault somewhere.
I have up to 200kHz working using your calculation. But, I cannot get 400kHz working - any ideas why? I have tried the bits you showed in the datasheet but no joy.
Hopefully, tomorrow I will resolve the test issue. I am sure it is something silly.
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
Very hard to complete operational and robust solution for TWI on these chips.
The worst TWI/I2C low level implementation that I have ever seen. I have implemented many but none as bad as this implementation.
The first thing that concerned be was the highly complex implementation of TWI in MPLAB-X - this was a red flag to me. Considering there is only four real registers that control the TWI interface the routines in MPLAB-X clearly mask some really complex low level ASM.
TWI Initialise
This command did not operate as expected. When changing devices the bus was left hanging and it required two programming attempts to release the bus.
Setting the registers as per MPLAB-X did not release the bus. Devices on the bus were confused. To resolve, to ensure the bus does not lock, I have added a manual STOP/START to ensure all devices know a Master is on the bus. I also repeat the initialisation if the TWI interface does not obtain control of the bus. There is an TWI0Timeout that can be tested upon exit.
I started with the design of state engine for the solution. States as follows:
START
SEND
RESTART
STOP
These match the register bits in the TWI registers.
However....
**TWI Start & TWI ReStart **
Set the two bits to send an address... should work.
The Send initial address is time critical. There is no bit that I could determine to find the address had been set. A static delay resolves. Disassembly of MPLAB-X HEX shows a static delay also.
The TWI0_MSTATUS is also not set correctly. There is a timing issue when reading. The TWI0_MSTATUS changes even after a static delay. By caching the initial status of TWI0_MSTATUS and then comparing resolves. The TWI0Timeout that can be tested to ensure this is not a blocking test.
Controlling what happened on the bus is now as expected using the TWI0_MSTATUS bits.
TWI Send
Set the two bits to send data ... should work.
Send the data and, again, TWI0_MSTATUS is not set correctly. There is a timing issue when reading. The TWI0_MSTATUS changes even after a static delay. By caching the initial status of TWI0_MSTATUS and then comparing resolves. The TWI0Timeout that can be tested to ensure this is not a blocking test.
Controlling what happened on the bus is now as expected using the TWI0_MSTATUS bits.
TWI Stop
Works as expected.
Once I had TWI0_MSTATUS under control - everything else worked. Including frequency.
All the I2C libraries will work as these support HWI2CSTART, HWI2CSEND and HWI2CSTOP and TWI0START, TWI0SEND and TWI0CSTOP are fully compatible and no library changes are needed.
Tested here using SDD1306 and I2C Discovery programs.
As far as the formula for TWI... ??? I can't guarantee it's validity. There is a calculation for T_Rise and I think it is assumed to be Zero in this formula. There are latency bits to be set in CTRLA... i think? I'm not having a lot of success getting the slave mode working. 400 KHz may need smaller values in the resistors... I'm using 10K right now, but only up to 200 KHz.
I'm glad you're having some luck!
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
A thought. If you are ready the status register then check if you get the bits set in a timely manner. There is nothing in the errata but I can see that reading the status is not consistent.
By setting the cache and then testing they will be the same, if they change - the routine exits. If they remain the same then TWI0Timeout will equal 255.
Either way - you know when this exits the status is stable.
I rarely use MPLABX for anything anymore. Sooner or later MPLABX will be all we have. I'll see if I can make heads or tails of it.
My assembly is NOT the way to go for Hardware i2C ... I have no idea what I'm doing or what a typical slave/master relationship looks like this early in my attempts. I have not attempted to do any bus error checking or timeout loops yet.
The only thing I can do reliably is set an address and read my 24LC256 EEPROM with polling only. I have a lot of time invested in I2C so it has to work for my own sanity.
Still have to watch the video.
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
Working on slave ATM, can't seem to get it to respond to it's address... so i'm not sure what is needed.
Focusing in on getting it to respond at this point. With most of the 'C' code I find, it does a couple of bus and error checks(collision , buserr) and then checks the APIF(address|stop). Guess I need to concentrate on that Slave code with the APIF detect. APIF can be either stop condition or address match which is determined by the AP bit in SSTATUS.
For me the problems usually avalanche and fall away if I'm persistent.
What's another day, right?
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
I have learnt the hard way.... what the logic of the C shows the actual implementation within the compiler can be totally different.
So, take MPLAB-X there is whole mass of ASM that is being generated by the compiler or the the compilation process. It is the same with GCBASIC. what the user enters in a high level language generates a lot more logic in ASM.
I would take a break. I will have a go using GCBASIC because I can add some much debug to figure out what is going on. I would approach this with a set of callbacks that hide any complexity required. I willing to have a go.
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
@cribcat
ADC Operations
Sucess using your ASM as the source and then adapting to GCBASIC.
This is a user program. This shows using
READAD()
andREADAD10()
. These are the GCBASIC public functions to read the ADC.The functions take a single parameter = AINn where AINn is valid specific to the chip in use.
Working example follows.
Support with A-D.h ( this lowlevel library) is this. I have documented for those who come this way in the future.
But, the highlights.
The crutial thing in all this. Is the user does not have to be concerned is AINn is PORTx_PinyCTRL, as this is actually very different across the range of AVRDx chips and would be very hard to navigate across chips. The table approach make use very simple.
Result on terminal
This is a full swing of the Pot - seems to work great. The two values are the 8bit and 10bit ADC.
The function
This does means another update to the DAT file - sorry.
ASM
The ASM generated is very nice and easy. I have listed here to explain.
Another brick in the wall. Done.
Thank for the ASM! It worked. The work was figuring out how to make this simple for a user.
Last edit: Anobium 2024-09-21
I have uploaded the a-d.h, DAT files and a tweaked ( not for AVRDX ) usart.h This needs to go onto of the GSTUDIO build from yesterday. This may need you to change back to mainstream to get the update.
https://1drv.ms/u/s!Ase-PX_n_4cvhYRZX6QjCzv3CKdcYw?e=ywLPX5
The demo ..\Demos\AVRDX\200_ReadADC_demo.gcb is there for you to try.
I tested down 0.4333 mHz on the chip frequency ( #chip mega4809 , 20/48 .. the compiler does all the calcualtions!) and this works very nicely.
Very simple to use.
DEV also gets the yesterday update, so it doesn't matter on what channel he is (well, just not on "no updates")
Try this for a bit of fun.
Change the
portf.5
to some LED port, and, change theAIN1
to something that has a POT attached.:=)
And, I was sent a message to optimise the new a-d.h, so, done.
Optional control constants
Adding additional constants to the user program can save up to 61 words but the functions are constrained to a specific PORTx_Piny
Set explicit port.pin - this will prevent any table lookup - would be used when either a single ADC is used, or, an error in the lookup table
* Saves 22 words but makes the READAD and READAD10 locked on the specified PORTx_Piny
* ANIx MUST be correct for the specified PORTx_Piny
Do not cache the port.pin setting. Therefore, user needs to manage the PORTx_PinyCTRL state.
* Saves 9 words
Change the ADC frequency. No program size impact. This just overrides the default value
#DEFINE AVRX_ADC_PRESC_DIV 16 // Options are 16 or 4
So, with these new constants applied the method looks like this - this would be fixed to a single AINx with no caching.
@cribcat
This week is PWM week.
I have started with fixed mode PWM. Historically, GCBASIC has supported a 38Hz, 50% duty on the first PWM port.pin. I have updated the PWM.h library to support this. Looks good.
The fixed mode PWM signal always generates a PWM signal as follows:
PWM Frequency 38Hz
PWM Duty 50%
Always on the first PWM channel
Directives are
#DEFINE PWM_FREQ 38 // this is the default
#DEFINE PWM_DUTY 50 // this is the default
Controls are
PWMOn
PWMOff
This program is for the mega4809
The PWM signal is generaed PORTA.0
PORTA.0 is the TCA0/WO0_Channel 0 output port.pin
The output port may be different for other AVRDx chips.
Developers:
A program looks like this.
~~~
#chip mega4809
option Explicit
~~~
The ASM is very simple. A script does the heaving lifting to calculate the register values and the prescaler etc. I have attached the ASM.
I will post updated pwm.h when I a few more capabilities added. :-)
And, adaption of the basic fixed mode. This changes the duty cycle and shows how to combine the ReadAD() and the Scale() function to ensure the duty cycle matches the frequency.
@cribcat
I am making progress on PWM and I will finish on Friday.
Have you made progress with TWI/I2C ? This is the last piece of work. :-)! Yipee!!
At the current moment I can point at a certain address and read 32 bytes(less or more) through polling the interrupt flags with my 24LC64 EEPROM. Currently I have turned my TINY1616 into the Master and working on code for M4809 Slave R/W. Error checking will be the last thing implemented. Many facets to the protocol and I'm not strong on it. There seems to be many possible ways to set up interrupts.
I have no other I2C devices to test with so I'm working closely with the Data Sheets and legacy I2C libraries and example code for reference (in "C") as well as the interrupt routine I wrote earlier this year. The app note cites the " Atmel Start " application configuration software for Microchip Studio 7... which is convoluted and useless for me.
Progress?... Yes. Speed?... Slow, Slow, slow.
Small victories to win , I guess.
Glad to hear of your progress!
G
@cribcat
Would you share how to init/send TWI data? I cannot even get the hardware TWI to flicker into life. You have done well to get this far.
Yes, I will post the ROUGH Master code for the MEGA4809... let me get it together.
This code sets(writes) an address of 32 in the 24LC64, reads the 32 bytes starting @ address 32, prints to USART3 , Waits 2 sec, Repeat. All done by polling the TWI flags.
There is excess code that needs to be trimmed in the " rdone_check: " loop. The program was malfunctioning on my tn1616. I will send updated code later after I verify that the new code works(better)on the m4809. After that, on to the Slave...
Sometimes the data sheet works against you...
New TWI read code
Thank you!
I have made progress now. I have the functions ready: TWI0Start, TWI0Send, TWI0Stop and TWI0Receive. They are working.
My test program is the same programs as I used for the Software TWI(I2C). They are:
1. TWI Device discovery
2. TWI GLCD
So, this will test all the functions.
Issues
I have one issue test issue to resolve - a silly error that I have not yet resolved is when TWI Device discovery discoveries a device it then thinks every device is present. Must be a bus fault somewhere.
I have up to 200kHz working using your calculation. But, I cannot get 400kHz working - any ideas why? I have tried the bits you showed in the datasheet but no joy.
Hopefully, tomorrow I will resolve the test issue. I am sure it is something silly.
Very hard to complete operational and robust solution for TWI on these chips.
The worst TWI/I2C low level implementation that I have ever seen. I have implemented many but none as bad as this implementation.
The first thing that concerned be was the highly complex implementation of TWI in MPLAB-X - this was a red flag to me. Considering there is only four real registers that control the TWI interface the routines in MPLAB-X clearly mask some really complex low level ASM.
TWI Initialise
This command did not operate as expected. When changing devices the bus was left hanging and it required two programming attempts to release the bus.
Setting the registers as per MPLAB-X did not release the bus. Devices on the bus were confused. To resolve, to ensure the bus does not lock, I have added a manual STOP/START to ensure all devices know a Master is on the bus. I also repeat the initialisation if the TWI interface does not obtain control of the bus. There is an TWI0Timeout that can be tested upon exit.
I started with the design of state engine for the solution. States as follows:
These match the register bits in the TWI registers.
However....
**TWI Start & TWI ReStart **
Set the two bits to send an address... should work.
The Send initial address is time critical. There is no bit that I could determine to find the address had been set. A static delay resolves. Disassembly of MPLAB-X HEX shows a static delay also.
The TWI0_MSTATUS is also not set correctly. There is a timing issue when reading. The TWI0_MSTATUS changes even after a static delay. By caching the initial status of TWI0_MSTATUS and then comparing resolves. The TWI0Timeout that can be tested to ensure this is not a blocking test.
Controlling what happened on the bus is now as expected using the TWI0_MSTATUS bits.
TWI Send
Set the two bits to send data ... should work.
Send the data and, again, TWI0_MSTATUS is not set correctly. There is a timing issue when reading. The TWI0_MSTATUS changes even after a static delay. By caching the initial status of TWI0_MSTATUS and then comparing resolves. The TWI0Timeout that can be tested to ensure this is not a blocking test.
Controlling what happened on the bus is now as expected using the TWI0_MSTATUS bits.
TWI Stop
Works as expected.
Once I had TWI0_MSTATUS under control - everything else worked. Including frequency.
All the I2C libraries will work as these support HWI2CSTART, HWI2CSEND and HWI2CSTOP and TWI0START, TWI0SEND and TWI0CSTOP are fully compatible and no library changes are needed.
Tested here using SDD1306 and I2C Discovery programs.
Done. I will publish tomorrow.
As far as the formula for TWI... ??? I can't guarantee it's validity. There is a calculation for T_Rise and I think it is assumed to be Zero in this formula. There are latency bits to be set in CTRLA... i think? I'm not having a lot of success getting the slave mode working. 400 KHz may need smaller values in the resistors... I'm using 10K right now, but only up to 200 KHz.
I'm glad you're having some luck!
Disassembling MPLAB-X generated hex may help. There may be a lot ASM that they add to help the operations.
What does the source in MPLAB-X reveal for slave operations?
I will post a video of TWI operations later.
@cribcat
Here is video showing Master - https://1drv.ms/u/s!Ase-PX_n_4cvhYhLZmFTURS68tnObA?e=Kjx8fF
A thought. If you are ready the status register then check if you get the bits set in a timely manner. There is nothing in the errata but I can see that reading the status is not consistent.
By setting the cache and then testing they will be the same, if they change - the routine exits. If they remain the same then TWI0Timeout will equal 255.
Either way - you know when this exits the status is stable.
I just tested by changing the test of Status to a simple set of NOPs.
Anything less than 70 in the loop gives the incorrect result after a write to .ADDRESS or .DATA
I will stay with the test of Status as this will support any frequency.
I rarely use MPLABX for anything anymore. Sooner or later MPLABX will be all we have. I'll see if I can make heads or tails of it.
My assembly is NOT the way to go for Hardware i2C ... I have no idea what I'm doing or what a typical slave/master relationship looks like this early in my attempts. I have not attempted to do any bus error checking or timeout loops yet.
The only thing I can do reliably is set an address and read my 24LC256 EEPROM with polling only. I have a lot of time invested in I2C so it has to work for my own sanity.
Still have to watch the video.
I wrote a state engine Slave for PIC. It has been used by many but this is PIC only.
Maybe it is time to write a AVRDx Slave using the same technique. I will get on is next month.
What is needed for AVRDx ?
Working on slave ATM, can't seem to get it to respond to it's address... so i'm not sure what is needed.
Focusing in on getting it to respond at this point. With most of the 'C' code I find, it does a couple of bus and error checks(collision , buserr) and then checks the APIF(address|stop). Guess I need to concentrate on that Slave code with the APIF detect. APIF can be either stop condition or address match which is determined by the AP bit in SSTATUS.
For me the problems usually avalanche and fall away if I'm persistent.
What's another day, right?
I have learnt the hard way.... what the logic of the C shows the actual implementation within the compiler can be totally different.
So, take MPLAB-X there is whole mass of ASM that is being generated by the compiler or the the compilation process. It is the same with GCBASIC. what the user enters in a high level language generates a lot more logic in ASM.
I would take a break. I will have a go using GCBASIC because I can add some much debug to figure out what is going on. I would approach this with a set of callbacks that hide any complexity required. I willing to have a go.