Right now I am working on the rewrite. Before we had massive if else chains
within each case of a switch statement to determine what each button does in
each state. This was messy and in that implementation only one button press
would be registered even if multiple were pressed in a short period of time.
To fix the overall structure I am having six different button events, one for
each button and then short and long press. We will then have an array of
function pointers that is indexed based on the state and the event. Each of
these will correspond to what each button press does in each individual state.
I will also being setting up a FIFO queue so that all buttons pressed will
register and will execute a refresh each. So now all the button interrupts do is
push their respective button event to the FIFO queue and the state machine takes
care of the rest.
I just wrote all the code to test this and only ported over the home menu for
testing purposes. Time to test!
So it seems to be working fairly well! Except it seems I cannot use WFI for
halting the while loop. I am not sure if we are having extra interrupts that I
don't know about, but it keeps passing that instruction even when there is no
button presses.
To avoid this I just added a flag for the number of interrupts that have been
triggered. When an button interrupt is called it increments this flag and once
we handle it, aka pop the event off the queue, we decrement this flag. When
testing earlier I only had it as a bool and this was still breaking when doing
multiple button presses, since the state machine would only run one cycle
instead of the multiple required, unless the interrupts were spaced far apart
enough to catch the flag when it was low and set it high.
I don't understand, BUSY is high for that long. But from the start of my render
function until after the wait for IDLE is only ~200ms. It is only in the wait
function for the time of my hardcoded delay. That means that the file loop
waiting for BUSY is not even doing anything??
So BUSY is ONLY high when the display is executing a render. If you are writing
to the display it does NOT say it is busy.
So I changed it such that I set a flag saying the eink is busy once we start
rendering. Then the flag is set low upon a falling edge interrupt of the BUSY
pin going low.
So finally the display is only halting the system while its actually busy and
not a hardcoded amount of time. But we are still having issues. If I execute
multiple renders back to back it can update pretty quickly. But if I try to go
at that same speed with the buttons it still is not working. I need to do the
refactor I am planning and see if that can help.
So the code for handling state transitions and button presses is hacked together
and messy. I think I can make it significantly cleaner and maybe faster too. I
am planning on having many different events. These will be specific button
presses based on the state you are in. Every single event will have its own
handler stored in an array. This will make it very easy to add new states and
control what each individual button press does within each of those states with
only a few lines of code. I also want to be able to coalesce button presses.
I am going to queue events (button presses) when they happen even if the display
is rendering. If there are multiple of the same in the queue (such as two down
presses) before a render is executed, we can then execute the logic and write
both of them to the framebuffer before rendering. This removes the need to wait
until the screen is fully refreshed before pressing the button again, and will
remove multiple renders happening for multiple of the same button presses. This
is particularly helpful when navigating the menu.
We tested the battery monitor PCB IC on the secondary I2C lines because Hannah
flywired it. It still reads 0. But if we hookup the module to those same lines
and use the same firmware we get the battery percentage. Therefore we finally
can confirm is is the battery monitoring hardware on the PCB and nothing to do
with the MCU, I2C lines etc.
We still have no idea what could be the problem with the battery monitor. We
think it could have to do with the I2C peripheral since we using a different
peripheral for the module. We had the issue with the SD card SPI where the pins
themselves weren't working until we did something special with them.
The SCL and SDA lines seem to be always high? That would mean nothing is pulling
them down. We are using 10k pull-ups, the module has 4.7k. So maybe the higher
resistance is making it harder to be pulled down and the MCU is unable to pull
them down to send a command to the monitor?
So after changing them to 4.7k we still don't see any changes.
We pulled out the AD2 and it seems that the MCU is sending stuff when on the
alternative I2C peripheral? I think we need to test with that one and finally
confirm what is up.
Similar to the SD card SPI we just initialized the I2C lines as outputs and
tried toggling them normally to make sure that they work. They seem to be
working. So I am not really sure why they wouldn't work for I2C???
So before my font renderer would only work when the width of the font was 8. I
had modified it to work with variable heights but the widths was not working. I
finally figured out why and updated the renderer. So if the width is greater
than 8 that means there is more than 1 byte for the width. So we have to have a
third inner for loop that iterates over the number of bytes in the width and
will draw the first one and then move the x pointer over by 8 and draw the
second one.
Also added setting for choosing small or large font.
Since the buttons are on the side of the device, it default to the buttons being
controlled with the users left hand. I figured that some people may not like
this. Therefore I added some functionality to be able to rotate the entire
display 180 degrees. Therefore everything will be flipped and you can turn the
device over and the users right hand can control the buttons instead.
Allowing them to choose their dominant hand is actually fairly easy. The eink
display we use has configuration options which allow the ability to configure
which way we write into the internal RAM and where we start. So I have it set to
start at the end of RAM and flip which way the x and y pointers
increment/decrement.
We found it annoying to have to remember, and sometimes forget, which button
does what. For that reason I have added little icons on the screen next to the
buttons which show what the button does, such as a little up or down arrow.
These button hints change depending on the menu, so in the flashcard menu it
will show arrows that point to the left and right instead.
We were having some issues with the packaging and struggling to fit everything
inside. The packaging people printed a spacer to extent the thickness of the
packaging. I met with someone from the packaging team to help them install that.
It makes things much easier now.
The way we have the buttons is pretty poor. We don't have headers for some
reason so we had to manually solder headers onto the individual pads. This leads
to them being very fragile. I accidentally broke one of these off. Luckily the
pad did not get pulled up. I spent some time soldering it back on, takes a while
when solo.
We are still having issues with our reset button. We desoldered the one that was
on there and decided to try another one. Zoe went in to lab today and was saying
that it said the STLink was unable to reset the micro. After looking at a
picture of how the button is connected it looks incorrect. It seems to be
connected in a way that is always pulling it low so we will fix that.
But we also thought not having populated our pull up resistor was causing
issues. After looking at the datasheet it seems that NRST has an internal
pull-up resistor. I had no idea about this. They claim its "weak" but its
40kOhms. I am not sure why we have the issues in regards to not being able to
reset without having the STLink plugged in sometimes... I thought this would
resolve it but since there is an internal pull-up, probably not.
Power switch stuff
I can turn off, but no interrupts are triggering in STOP 2 mode.
For now lets just not do lower power mode for MCU...
Right now I am working on getting it so we can display if the battery is
charging or not. It seems that the battery charger exposes a pin called LBO that
goes LOW when the battery is being charged.
I have just set up this pin to be an input and made a function to read it in. I
am displaying the string "CHRG" near the battery percentage if the battery is
currently charging.
I also merged all of these UI, RTC and SD card changes. Once Zoe finishes
parsing the number of cards I can display that on the UI. Also once we solder
buttons back on I can test saving and displaying the last studied date as well.
RTC working! Had to set some extra enable bits and turn LSI on. The RTC APB
enable bit is not the only one, there is another RTCEN bit as well. On top of
this you have to do a backup domain reset first which I did not realize at the
time.
Why don't I need to do BCD->Dec when reading?
One thing I am really confused on is the format of what is stored in the RTC
registers. I have to convert my decimal input to BCD to put them into the RTC
registers. But when I read the data back I do not need to convert back to
decimal? When I was just trying to do that I was getting invalid data. I tried
removing the conversion and it somehow was working?
Date seems to be 0 even though I am setting it. Upon further testing it is only
zero on the first read, this is odd, because even if I give it a delay its still
on the first read. For now I am just putting a dummy read right after
initialization. Not the best fix but we don't have a ton of time to get to the
bottom of it right now.
So the SD card is not mounting for some reason. We have soldered it on and it
looks like its getting the proper voltage. For some reason it looks like the SPI
pins are not even doing anything?
To test I am trying to just enable CS as an output and toggle it in main and
read it from the scope, its literally not toggling. But if I do the same logic
to toggle the LED it works just fine? I have no idea why we cannot even toggle a
GPIO pin
So for some reason GPIOG 3-15 are electrically isolated by default. You have to
set a bit in the PWR peripheral to get rid of this isolation. All of our SPI
pins were in this range, that is why they were not working. Thankfully Zoe
searched it up andfound this forum post
which told us about this isolation.
Looking into LFN since Zoe mentioned it as an option for num card stuff, also
gets around only having 8 char deck names. Upon looking into FAT more it seems
that the reason for the 8 char deck names was because fat only allows 8 char for
the actual name and then reserves 4 more chars for the file extension, Zoe
figured this out.
I am now looking into RTC since we want to show the last time the user studied a
specific deck and hopefully track how long they have studied overall. As of now
I am going through the initialization sequence given in the datasheet, this may
take a while.
So I have written the entirely of this initialization sequence and wrote some
code which I think will read it back when we want to see the value in the time
and date register. Upon testing nothing is really happening though. So it looks
like it is getting stuck on waiting for the INITF flag, this means
initialization is not finishing? I must be missing something but man lab is over
so I will look into it tomorrow.
Mark got the buttons soldered onto the PCB. So now I have to start testing them
with the firmware. We firstly checked to make sure the buttons are working, we
measured voltage across them. It is ~2.9v, this is within the range of it being
a 1 so that is okay.
The interrupts don't seem to be triggering though? Not really sure what is going
on. The waveform of the button is excellent so the debouncing circuit is working
properly. We also just measured the voltage on the actual microcontroller pin
and it is being propagated there as well.
Okay so it turns out I just forgot to call the function to initialize the
buttons. I had this commented out before the buttons were actually installed,
whoops.
So now that they are triggering both 4 and 5 are constantly triggering, this is
taking up basically all CPU time and even the screen won't render. I am clearing
the pending bit so it can't be software. We know the waveforms are good so what
is going on? Okay it turns out that I was indexing the SYSCFG registers wrong..
I accidentally increased the index so it must have been some sort of undefined
behavior.
I was trying to draw the image
The team wants to add UI stuff so I am looking into drawing an image on the
display. At the minimum I wanted to draw the sleeping emoji when the device is
sleeping. It is actually not that difficult, we just have to go through bit by
bit of the image and write it in the framebuffer, I was forgetting to divide the
index by 8, that is why this took me longer than it should have.
I want to center the menu on the display. I need to figure out the best way to
go about calculating the offsets. Right now I am attempting to get the total
height of the menu based on how large each item is and how much spacing is
inbetween each item. I can then subtract this from the total height and, I
think, divide by two to get the starting y height.
Got stuff hooked back up, still works. messing around with UI, merged PRs
So we are getting ready to get the preliminary checkoffs. Zoe and I hooked up
the battery and 5v from the power supply and both seem to be working and give
3.3v output.
We tested flashing the microcontroller and writing to the display and all seems
to still be working on that front as well. Waiting for TAs to come and we will
get check us off.
I would like to make some UI changes. Right now we have to go through all kinds
of complicated button mappings to be able to go to different menus. I think the
solution is just to have another menu at the start where one can select
studying, download, settings etc.
I just merged the newpins PR and my button refactor PR, I had to resolve some
merge conflicts but they were fairly simple.
I implemented the new menu, its rendering properly but I am not able to test to
make sure I can properly navigate to different states based on selection because
we do not have buttons hooked up on the PCB yet.
Hannah soldered the MCU yesterday since we confirmed that we are getting a
consistent 3.3v from our power system. Before powering it on we of course needed
to make sure there were no shorts. So we have started continuity testing and our
first short seems to be on 3.3v and GND?? It doesn't look shorted though.
So all of that 3.3v and GND are shorted together, we just realized that means
that if only one is shorted that all would be shorted together internally so
thats why the continuity test was beeping for all of them.
List of shorts:
30-33 shorted (GND and 3.3v)
38-39 shorted (GND and 3.3v) ** possible short, check after 106-107
71-72 shorted (GND and 3.3v)
83-84 shorted (GND and 3.3v)
106-107 shorted (GND and 3.3v) **** CLEAR SHORT, fix this and retest
120-121 shorted (GND and 3.3v)
We removed the clear short on 106-107 and now its all resolved! Hannah did a
great job, soldering all of those pins and only having one small short.
Mark soldered on the programming header and now we are able to attempt to flash
the MCU. I wrote a basic blinky program to blink one of the LEDs on the board as
an initial test.
So when I try to flash the code it says successful but nothing is actually
happening with the LED? When I try to erase the flash it also says that the NRST
pin cannot be pulled low by the ST-link.
They said they didn't solder on the NRST circuity yet, doing that now.
So we put the button on and it seems its reversed, the default state is pulling
down...
We put a different button on with fly wiring and were able to get that fixed.
Now we can flash the MCU with the LED blinky code and it works!!!
We just soldered on the e-ink header and we can write to the e-ink too. This
works with 5v being supplied AND off of the battery power!
We probed the PG and STAT pins on the charger and they are all giving us the
correct statues, telling us when the battery is being charged, when USB vs
battery is powering etc
I am looking into some more power stuff, I wrote functions to shut down what we
are able to and then enter the low power mode for the MCU. I just need to test
this and also set it up to actually be triggered by the power switch. Right now
we don't have that set up on our prototyping board so I have not written the IRQ
handler yet.
But it is fairly simple, on a falling edge I just call my power off function and
on the rising edge we call the power on function which just resets the MCU.
I am not sure on some optimization stuff. Because I am not fully sure what
exactly will lead to the best power conditions, I think we should probably turn
off all clocks and set pins to analog etc.
I also reviewed the PR that Zoe made for moving over the whole codebases pinouts
to be for the PCB, as most of the pinouts are different from our devboard, also
have a slightly different MCU. She made some more changes after I left
yesterday.
I am working with Zoe on integrating the battery monitoring that Hannah wrote.
They currently have it set up to read the state of charge. I am not sure what
that is but it is saying 72% while we are also reading ~850, but the battery
is 2000mAh. This does not make sense.
We are pulling all of the data possible to investigate. There are tons of
registers we can read from including voltage, state of charge, capacity, full
availability, remaining capacity and more.
So it turns out that the battery is just bad, the full charge capacity was only
~1200mAh, so that makes more sense and it does seem that the state of charge is
indeed the current battery percentage. That is all that we will need in our
firmware.
We aren't fully sure when the MCU will be on the PCB and be ready to test, but
we are starting to prep for when that comes. For prototyping we are using the
STM32L432 and in our final design we are using the STM32L496. Therefore we have
to go through the entire codebase and update every single pinout to match what
is final on the PCB.
This took a bit longer than expected, Zoe and I just did most of it together
(pair programming). We had to edit the majority of the files.
Since our hardware testing is a delayed I have decided to start adding new
features to the device. Today one feature I want to polish is deleting decks.
I recently did the whole refactor to make it easy to add features like this,
so we added where if you hold down the select button on a deck then it would
delete it. But a user could accidentally delete a deck this way. So I want to
add a confirmation menu so they can be sure they want to delete it.
This was fairly simple, I just needed to use the new button framework and create
a new state since there is a new menu.
While I still have time I also want to add an option for users to choose
settings. The only two options I can think of right now are shuffling decks and
a learning algorithm.
So to go to this settings menu I am trying to be able to hold down the forward
button to get there. For some reason once I hook up the debouncing circuit to
that button everything freezes up? I don't really know what is going on there.
Other than this though it works. I can toggle if I want to shuffle the decks or
not. I have not actually implemented the learning algorithm but if/when I do,
there is the ability to turn it on and off now.
So now the settings are saved into RAM which I believe persists in Stop 2 mode
as well (I will check on this). I do need to write them to disk though in case
the device dies or if we were to implement fully power down in the future.
I checked and SRAM does persist in Stop 2 mode. So if we run out of time we can
just not write these settings to the disk and have them persist in RAM. This is
obviously not a good idea for a product that is being shipped, but it would be
corrected later.
Could the forward button not working for debouncing have something to do with
the fact that it is one of the ones that works for multiple interrupt lines
(EXTI 5-9)?
Okay so I just tested it by hooking it up to another buttons debouncing circuit
that I know works and we had the same issue with the constant triggering.
Okay so I checked and somehow once you press the button hooked up to PB6, the
ouotput stays high for some time. I tested this with the debouncing circuit too
and it stays high for a very long time, probably because it charges the
capacitor up a lot from staying on longer?
So the issue only occurs once I initialize the eink display. Also PB6 gets a
pulse every time the eink writes? Is it somehow hooked up to eink busy pin?
Maybe something with that is why we needed a delay in the wait busy function?
Something weird is going on here... I need to try probing eink busy along with
it and see if its somehow the same.
I just tested it and when the eink busy pin goes low (not busy), PB6 goes
high. How is this possible. I don't know if it has anything to do with it but
the eink busy signal is very noisy.
Wait I think I may be dumb, pressing the button itself doesn't mean anything as
it makes a screen update which would make the display busy. But it does happen
upon reset which is the weird part.
Also pressing any button seems to be bringing PB6 high. This means that after
all maybe busy and PB6 connected somehow??
Today my goal is to setup holding down the select button to delete a deck off of
the device.
To get this to work I need to refactor. Currently the button handling is a mess.
Right now in each handler we handle changing states. Within the state machine
case statement we don't actually do any state transitioning. In the beginning
this was fine, it worked but wasn't the most ideal. When we try to add new
features we have to hack it into that current setup.
The ideal setup (which I am currently implementing) is to have the button press
and its type registered somewhere. For example all the button ISR would do is I
hit the Select button and that it was a long press. Then in each state of the
state machine I can handle each button and press type combination. This makes
things significantly more modular. We will be able to add anything we want and
have it be triggered by button presses and not have to hack it into the button
ISRs.
So I have moved everything out of the button handlers now. All they do is
specify which button was pressed and if it was short or long. This opens up a
whole world of possibilities now, we can do basically anything with the buttons.
These changes made adding deleting decks trivial. I just had to add a call to
delete_file() in the long press section of the main menu state in the state
machine.
Right now I have basically have a mutex in the button ISRs because we did not
have debouncing before. I presume that this will be able to be removed once all
of the buttons are debounced. I need to verify this though, as I tried removing
it from one debounced button and it seemed to cause undefined behavior.
So we are starting to realize that we did not fully think about power
consumption and shutdown properly. Originally I think had suggested we do some
combination of bulk capacitors and have the power switch be managed by software.
I am not too sure what I was thinking because this is not the place to use bulk
caps.
We ended up going with a fully software based power off system. We figured that
we would be able to put everything into a sleep mode of sorts and have power
consumption be negligible. The reason for this was in case we needed to have
some time to clean up the software being actually losing power. It turned out to
be a poor choice and a learning experience.
It turns out that we cannot actually sleep some things such as the SD card. We
should have done one of the following options:
1. Had the power switch directly be connected to hardware with bulk
capacitors and be able to read it via GPIO. This would make it so that once
the switch is flipped software can realize that and quickly clean up before
the bulk caps run out and the system powers down.
2. Had the power switch be fully handled in software BUT have MOSFETS to
turn off power to each component. We could have controlled power to each
component with MOSFETS, although in retrospect this option is still worse
than option 1 due to extra software and GPIO requirements. Option 1 would
have been the best.
During our testing we found out that the SD card reader consumes 3.5mA during
idle and ~4mA during a read. Since we don't have a way to power it off via
software or hardware we are stuck with it always running. This will make the
battery drain quickly on our device. If we make a PCB revision or were to
actually go to market with this it would of course be resolved.
The app doesn't seem to be able to update flashcard sets, aka reupload the same
one with changed components.
Turns out we were appending to the file on the SD card instead of overwriting
it. The SD card flags are weird so we are just removing the file and then
recreating it as there doesn't seem to be a way to overwrite unless we are
looking over it. This seems odd though.
I am currently getting a bit side tracked with feature creep. I implemented the
ability to shuffle the deck so one can study the deck in a random order each
time. The order is different each time they open the deck.
I am also currently also working on a "home" page for each deck, where settings
can be selected such as the ability to shuffle, maybe see stats, etc
I am starting to realize how poorly and non-modular this code we wrote is. A lot
of stuff is hacked together to get it working instead of making it well thought
out. Therefore it is hard to add new pages, menus, etc.
It would probably require a refactor to be able to display a new menu that
allows the selection of randomizing or not, showing deck stats etc. If we do not
do this I don't know the best way to do randomization. Should it be the default?
I have begun looking deeper into the sleep states and working on the code to
actually get it working.
Upon looking into it, its fairly simple. Stop 2 just needs us to set our desired
low power state to Stop 2 in the power control register. Then setting the deep
sleep bit in the Cortex M4 System Control Register and then calling WFI. Once we
do that we will be put into Stop 2 mode.
The only problem is that in Stop 2 mode ANY EXTI interrupt will trigger a
wakeup. So we need to disable everything that is not the power button.
For the power switch we will need both falling and rising edge triggers. The
falling edge trigger will ensure that everything else is put to sleep (display,
sd-card reader, battery monitor etc), disable all other interrupts and then go
into Stop 2 mode. The rising edge will wake the MCU back to Run mode. In this
part of the ISR we will also call a system reset from software. This is so we
can go back to a predictable state and have everything be reinitialized again.
Today I am starting work on the power switch logic. This is not something we
have prototyped so I am hoping everything will be okay with it.
As I am looking at this we may be in some trouble. So the lowest two power modes
are shutdown and standby, we don't need to save anything as it is supposed to
simulate a full power down so we probably just want to do shutdown mode, this
only uses 0.23uA of current!
So upon further inspection we cannot use shutdown mode. The only way to resume
execution after shutdown mode is by one of five specified wakeup pins. We did
not know this before and therefore did not configure our power switch to be on
one of these wakeup pins. This may mean we have to give up a lot of battery life
as we cannot access the lowest power modes.
Thankfully we are saved, there are some low power modes that accept any I/O pins
as wakeup pins. And thankfully one of these, Stop 2, is still fairly low power
at 2.86uA, so the difference is practically negligible.
I worked on actually implementing the state transition logic into the long press
vs short press logic. Now we are able to hold the back button to go back to the
main menu or go to the download state if we are already in the main menu state.
I had some issues because I forgot to say that a render was pending outside of
just the short press. Therefore after the long press it was actually updating
the internal state but the display was not rendering what was updated
internally.
I am in lab now trying to test this long press vs short press. But now for some
reason the interrupts are not even triggering, or atleast the display is not
updating.
Interesting, so if I try to clear the pending bit at the end of the IRQ handler
that is when problems arise, but if I do it in the beginning like before its
fine.
I spent some time looking at the footprint verification and noticed that all of
our headers were too small. I told Mark about this and he rerouted a few things
and made the header/connectors normal sized.
So right now with the non-debounced buttons if you hold down the button it keeps
going into the interrupt over and over. This doesn't make sense because it
should only be on the RISING edge, not always when its 1. Maybe this is because
its going 0 to 1 really fast because its bouncing? But it should end up being
stable after some time, unless we are doing micro-movements that are making it
bounce.
So I was really struggling with debouncing the button for some reason. Zoe
helped a lot and we ended up figuring it out. I think part of the reason was
that the pin we were using, PA2 was used in the built in st-link... So that was
causing some interference.
Now we are able to detect long versus short presses. I just need to work on
setting up exactly what the long presses will do depending on where in the menu
the user currently is.
So I have decided to just hijack the timer that I use for delays to also use for
detecting long presses. It is setup to always be running and to have one tick be
one millisecond. Therefore I can just do similar to what I do for the delays,
and grab the start and check how much time elapsed by the falling edge.
I also spent some time considering the state machine. I would like to use have
the state machine only run if there is an interrupt, aka run the WFI macro at
the bottom of the infinite loop. But the problem is that the download state
should just go straight to the main menu and doesn't need a button press,
therefore there is no interrupt. I am trying to figure out the best way to
resolve that.
Right now I am thinking of just telling the user to press any button to
continue, that is seemingly like the best solution at this point.
I am getting started working on fully navigating the menu.
Right now we are unable to access all parts of the menu
without modifying the code. This is because we only have 3
buttons and all are
being used.
The two options are as follows:
1. Double/triple pressing:
- This is good as it is easy for the user to understand, they just need
to press a button two times to go back to the main menu, instead of
the one time for its primary function.
- The downside of this is having to wait a fixed interval. So after the
first press, if there is another press shortly after it can be
determined as a double press. But if the user wants to do a single
press then there will have to be a waiting period to wait for a second
press regardless. This will be a source of input delay and that takes
away from the user experience.
2. Long pressing:
- This is still pretty good for the user, they will have to hold the
button down for a fixed interval. This will probably take longer than
it would to just double press the button quickly, but this extra
functionality is only used for going back in menus or to special
menus. This is not something that the user will be doing often.
- This removes the downside from double pressing. This is because once
the button is let go, we can instantly determine if it was a short or
long press based on how long it was held. Therefore we do not have to
have any input delay in the majority case (short/single presses).
So because of the pros and cons we are going to go with long pressing.
Originally I was thinking of implementing this similar to how Niraj does
de-bouncing in 362. Constantly storing the values of the button, if it becomes 0
at any time then we can see how much time elapsed and determine what to mark it
as. Niraj had mentioned this during our design review.
I have now thought of a cleaner solution. We can setup the buttons to have both
a rising and falling edge trigger. On the rising edge we start a timer, and on
the falling edge we check the value of the timer (aka how much time passed).
This completely negates the need to poll the button and check when it is
released.
I am trying to get variable fonts to work. I wrote it the other day but I was
unable to test it. My algorithm seems to still be working for 8x8 but not for
anything that has 2 bytes, so anything bigger than 8x8. I did get some fonts
from the Linux kernel source code that will work though!
Right now I made a modification that takes into account the bytes per row so it
can actually grab the correct character. Because anything bigger than 8x8 will
have more than 1 bytes per row so it needs to go deeper into the array for each
character.
Turns out I need to go through the for loop extra times if there is more than 1
byte per row. So I just multiply the height by bytes_per_row!
This is messing up my wrapping function though. Maybe I am not properly taking
into account the width of the font? I think that the centering is only messing
up for single line strings (no wraps).
So the reason it was all messed up was because I accidentally switched up the
height and width. Once I fixed this my code started working again, I had to
revert some changes that I was making and not understanding, I was not
understanding as they were wrong since I switched height and width.
I recommended to the team that we set up a to-do list with the remainder of the
things that we need for the semester. I worked on thinking of firmware side
things that we need and added them to the todo list.
I have been working on getting variable fonts set up. I am unable to test it as
I am not in the lab. Right now I have grabbed a few different sized bitmap fonts
from the linux kernel. I have also modified every UI/draw
function to take in a font struct that stores the font array, height and width
of the font. The draw_char function was modified to read these height and widths
and use them instead of the hard coded values.
Fixing .txt stuff in app. Right now we have the files named as .txt for some
reason and the log files are also named with .txt, this means that our log files
and others will show up on the list of decks to select from. We obviously don't
want that so in the firmware we need to ONLY parse and display decks that don't
end in .txt so that we can keep our log files with the .txt extension. Not
having a file extension on the decks also allows us to have longer deck names
and then we don't have to strip off the extension before displaying them.
We don't have this fully working yet, we only have it setup on the firmware
side, need to set it up on the application side before actually enabling it.
I am prepping for a demo video that we need to make for the presentation. I have
currently added a counter showing which card you are on in the deck of
flashcards and then also added bounds checking so you can't overflow the menu or
the deck. I am also messing around with figuring out the best way to go back to
the main menu or start downloading a deck. I tried adding a download option to
the main menu but that isn't seeming like the best option.
Maybe we can add a whole separate menu in the beginning to download, go to
settings, or view decks. Settings can include toggling a learning algorithm,
randomization of decks etc.
It doesn't seem like the MSI clock frequency is actually changing when I change
it according to the reference manual. When I call the CMSIS
SystemCoreClockUpdate() function the SystemCoreClock variable is still set to
4MHz when I am setting the MSI to 16MHz? Maybe this is why my timer delay was
not working, it was assuming 16MHz. So I realized that by default the MSI clock
range is taken from RCC_CSR and not RCC_CR. I do not know why both have an
MSIRANGE field but I changed a bit in the RCC_CR to make the range be taken
from RCC_CR, where I was setting it. This in turn fixed my delay issue too,
along with refreshing the timer to make sure that the PSC and ARR are properly
loaded.
String wrapping isn't fully working. For short strings it is wrapping when it
should not. Looks like I left remnants of centering in the wrapping function,
this is not needed anymore because I have a whole centering function now. So it
was trying to center twice which was causing shorter strings to be cut of way
earlier than they should have.
Still running into some race conditions of sort when the buttons are pressed too
often. Maybe we should move all rendering into the interrupts? I just don't like
the idea of interrupts lasting a whole second each...
Now downloading a deck isn't working for some reason? I am confused... It is
stuck in the loop reading the filename, so its never getting the delimiter.
Okay so this was a wild issue. As I said earlier the clock was actually at 4MHz
and now its actually at 16MHz. I modified our UART config to be setup for 16MHz
as for some reason it was configured for 4MHz before. So how our receiving works
is that it reads in the filename and then afterwards reads the contents into a
separate buffer. I declare two buffers, one for the filename and one for the
file contents, I zero initialize both of these arrays. I was declaring the file
contents buffer and zero initializing it in between receiving the filename and
the file contents. For some reason when we were running at 4MHz this was working
just fine. But after switching to 16MHz this somehow caused enough of a timing
issue to throw things out of sync. If I move the declaration to before we start
UART stuff or do not zero initialize it everything works fine. I bet the
compiler generates a memset call or something similar. But I don't know why this
would cause an issue with 16MHz and not 4MHz clock speed, if anything I would
think it would be the opposite?
It still isn't writing properly? It seems to be getting the EOF on the second
character. We receive the first character of the file contents which is a '{',
but after that nothing seems to be happening. I don't think it is staying inside
of the loop so that means it must be receiving the EOF character (0x00 aka
NULL).
File IO during UART is BAD. Even opening a file in the middle isn't working??
Okay so I figured out the problem. The way UART is setup on the sending side is
that it just sends the entire payload at once, this is the filename plus the
contents. The way we had this setup on the receiving side was to receive the
filename, open that file and then read in 1024 byte chunks into a buffer and
then write that buffer to the file upon it being filled. This will not work due
to timing issues. We will get out of sync after even just opening the file due
to how long file I/O takes. So we have two options:
1. Put the entire file on the wire at once and store this into a massive
buffer on the receiving side to write to the file. This is the easiest
option, but we need to see if we have enough RAM to do this with our largest
files. I believe that we will.
2. The smart and efficient way is to send the file contents in 1024 byte
chunks on the sending side and have the receiving side ACK these, once the
ACK happens then the sending side will know its time to send another block
of the payload. This just adds some complication and back and forth but is
not too difficult to implement. And we would have ACKs t know if the device
was actually receiving or not.
So now we are receiving but its not writing to the file? Turns out I was not
closing the file... I completely forgot to do this.
Now the back button is not working. Weird because I thought it worked on PA2
before? It is weird, we probed the pin and it is ALWAYS high, even when we
remove any stimulus to it. This is even more weird as this is an INPUT pin?
Maybe its conflicting because its UART_TX? But we aren't doing anything with TX.
I am working on cleaning up the codebase a bit as it has gotten a bit messy from
a lot of quick prototyping. Mainly modifying UI stuff and making sure I am
following some standard. For example, the drawing header function should not be
called inside of *some* other drawing functions (such as main menu or flashcard)
like I had it, I either need to do in all of them or in none. I am going with in
none as there are other times where the header needs to be manually drawn at
times (this may change).
For some reason we are unable to open the file for writing. I was not null
terminating the filename. When reading it in we are reading into a buffer that
is the max possible filename length but it may not actually be this long of
course, I into a buffer that is the max possible filename length but it may not
actually be this long of course, I into a buffer that is the max possible
filename length but it may not actually be this long of course, I into a buffer
that is the max possible filename length but it may not actually be this long of
course, I had forgotten to add a null terminating character to the end.
Now we can't even receive the data properly? It doesn't seem like we are getting
anything after the filename?
I have been talking with Zoe about starting to get file transfer working between
the PC application and the device. I have written up what comments that explain
what the receive side should look like. We will first disable interrupts as
nothing else can happen while we are downloading a deck, then we will receive
the decks name and then a delimiter. After this we will read in the file
contents until we get some sort of EOF flag. Then once everything is read we
will store it to a file on the SD card.
I wrote this up, we are using ESC (0xBC) as the delim between the filename and
contents and NULL (0x00) as our EOF. This was fairly simple as I just
implemented what I described above, no majors issues thus far. I was able to
print what we received onto the display and it *looks* right. We have not
tested to make sure that it is actually writing to the SD card properly and that
it can be parsed properly yet.
So Zoe and I are meeting up to get started on integrating the sd card and eink
display. Right now I have just been hardcoding stuff for testing, now we can
read and parse flashcard decks from the sd card to put on the display.
Merging both branches to main and then creating a new branch from that, luckily
no conflicts because we were both working on completely separate subsystems.
So Zoe wrote most of the functions to read from the SD card and parse them
properly into our deck structure. Looks like we do not have a function that we
can easily call to be able to get all of the files. We just wrote one it is
fairly simple as all of the files are in one directory, it just loops through
all of the files in a directory and writes them to an array.
For some reason the array is not actually getting filled though? We don't need
to pass the address of the array since they decay to pointers, not really sure
what is going on here.
Okay so it turns out I was doing char* decks[NUM_DECKS] instead of char
decks[NUM_DECKS][LEN_DECK_NAME]... So it was unallocated memory, this was a very
silly mistake.
Wrap strings on flashcards in center can do this with strlen floor division.
width to determine number of wraps to determine what line to start/end at. This
works for centering horizontally, but I am having trouble centering them width
wise. Okay that actually wasn't that hard, I just need to subtract the max width
from the width of the string and divide by two, and if its very long and will
wrap then we just leave it alone and only wrap horizontally.
I setup the clock to be 16MHz. By default the MSI is set to be 4MHz and that is
a bit too slow for us with reading from the SD card and things of that sort, so
I increased this to 4MHz. I also just finished setting up an actual delay
system. Before I was just having a while loop decrement a variable but now I
have setup TIM2 to be in a sort of free counting mode. We can just compare the
current CNT with the CNT from the start. I found out that setting ARR to
0xFFFFFFFF is the best option as it will keep counting up for a very long time.
I just finished implementing scrolling on the main menu. The main menu can
display up to 6 decks of cards, but if there are more than 6 decks we need to go
to the next page. I implemented this by just changing the offset into what is
displayed by checking if current selected deck on the menu goes above a multiple
of the max decks per page (6), so basically floor division.
I am going to start looking over and reviewing our schematics as there was some
things missing on them previously, want to make sure there isn't anything else
important missing.
Schematics look great so far, only a few small issues such as not grounding the
programming header, I think Mark and Hannah will be able to get started on the
PCB layout soon!
So for some reason PB7 isn't working for the backwards/down button? I have
switched select to PB0 and forwards/up to PB6 so that I don't interfere with
Zoes SD card pinouts so that we can try integrating our stuff together tomorrow.
I have no idea why PB7 specifically wouldn't be working? Could it have something
to do with the shared interrupt handler? I should probably test some other
button in the 5-9 handler to see if it causes issues too.
Right now I am working on getting menu selection fully working. For some reason
when I try to select a deck from the menu it will re-render the main menu upon
the button press but update the internal state for the flashcard navigation?
This must be a race condition with the state machine?
After some messing around I added a 10ms delay between each loop of the state
machine logic. There is no need for it to run that much anyways, this should
hopefully give the inputs time to settle before rendering.
I was thinking about the best way to go back to the main menu with only three
buttons. After talking with Hannah, mid conversation I realized that for a long
press if the button goes low quickly while waiting (like a single press) then it
can just move on. Unlike a double press where you have to wait the whole
duration of the delay to make sure there isn't another press. So we will go with
long pressing the button to go back.
Also I am starting to wonder where we should display stats such as the number of
cards in the deck and things like that, right now I just go straight from the
selection menu to the first flashcard, no place for seeing stats, do we need
this?
I have been experimenting with how long drawing to the framebuffer takes.
Obviously rendering is the longest part, but this can happen independent of the
CPU. We can have DMA write the data over SPI and then the e-ink does rendering
on its own after that. Therefore we could use that time to predraw the next
framebuffer so the only perceived delay to the user is the actual render. I am
unsure if this is possible as of now but I am still looking into it.
I got multiple buttons setup. I am as of now able to move both ways through the
menu and select a deck of cards, now need to get the info from the SD card and
see how we are going to have it formatted.
I want to figure out how to speed up rendering stuff even more. I think that I
can do some sort of double buffering, basically load up what would be in the
next buffer early and render that one next? I am unsure if I can know what would
be in that buffer though, but that would make it so that I don't have to wait
until the write to the framebuffer is done to start the render. I am not even
sure if writing to the framebuffer early will even help, maybe it is not not
that slow of an operation?
I haven't really been looking into this part too much, I will look into it more
a later time as it isn't super important. As of now I am working on rendering
the flashcards and wrapping text. Wrapping text is annoying because I have to
wrap based on the word, just wrapping around the screen based on characters is
simple, but not as simple for words.
I have been working on getting pinouts organized for our prototyping dev board
to make sure that everyone is using different pins so that we can start to
integrate easily. Because SPI3 uses the GPIO pins
that I was using for buttons I need to switch. Some free pins that I can use are
PB0, PB6 and PB7. But for some reason when I try to switch to these pins
everything stops working? The only thing I change is my button_init() function
and now even SPI doesn't seem to be working as I can't write to the display.
My e-ink pins are all on Port A so I am unsure why this could even be impacting
anything?
I have been doing a lot of thinking about how to handle button presses. So far I
have setup EXTI interrupts and am testing navigating through the menu, but this
doesn't seem to be the best way to do things. If the user presses multiple
buttons back to back and the display renders after one, then either multiple
renders would need be to queued up or the display would not be displaying the
correct info. Both of these are bad, pressing the button multiple times should
not need multiple renders if it happens relatively fast. Also if the user has to
wait until the screen rerenders to navigate down another menu option again, that
will take a long time to navigate their decks if they have multiple decks stored
on the device.
The only real way to avoid any race conditions between the internal state and
what is shown on the display is to halt all button presses during a refresh. But
then the problem comes up of how many button presses can happen before a
refresh so that we can allow multiple. Or I need to come up with another menu,
or add partial refresh support.
Okay so for now I set up the fastest refresh mode, this will make refreshes take
~1 second. This will solve a decent amount of the problem. Right now I only have
button presses actually do anything if the display is NOT rendering. Therefore
the user can spam the button all they want but it will not cause race
conditions. I basically just a mutex that is checked in the interrupt handler,
if its not set then the handler will take it and it can only be cleared once the
rendering is completed (all commands are sent and then wait the 1 second it
takes to complete the render.)
For some reason when I try to draw a line or something below ~160 on the y axis
it breaks and wraps around back to y = 0 and then also shifts over by some x
offset? I am unsure of what is going on with that as of now.
So the issue was just that I was calculating the index in a 16 bit integer and
the value can be upwards of 120,000, so it was just cutting the top half off,
which makes sense based on where it was failing to draw below.
I also got UI stuff setup. The ability to draw text within rectangles and have
it centered and things of this sort, this will be necessary for making the main
menu and things of that sort.
I got font rendering working! I found a good 8x8 bitmap font which is exactly
what we want and then the algorithm to render this font.
The algorithm is very simple, you just need to select the 8 bytes corresponding
to the character (the array is setup in a way so you can just index by the ASCII
value) and then iterate over the x and y components of the 8x8 character and
check each bit of those 8 bytes to determine what pixels need to be turned on.
Uhh good news, its kind of working? I can sort of clear and write but its still
a bit scrambled, but its not just random anymore. So apparently the DR register
is 16 bits and if you write less than or equal to 8 bits then it will pad the other byte with 0's.
I had no idea this was the case and ended up finding a forum post that led me in
the right direction along with reading through a large portion of the SPI
section of the MCU data sheet. This took way too long to figure out. I noticed
it because on the AD2 logs and on the scope it was showing something like:
0x12, 0x00 when just sending 0x12 and it was not doing this on the Arduino.
But now its almost like the burn in is getting worse when using it with the STM
but then mostly goes away when I clear it again with the Arduino? When the STM
is flickering to refresh the whole screen does not go black, only a scrambled
pattern. Therefore it doesn't seem like its refreshing the whole screen for some
reason. Some pixels do seem damaged unfortunately...
So the display is slightly damaged but it should not be a problem for now, very
slight burn in.
BUT I GOT IT WORKING. Zoe found a GitHub that sends command 0x71 in the BUSY
wait function, this command is not in the datasheet. It is something about
getting the busy status? But we can't read so I have no idea what it does, I can
find no documentation on it. But sending this command in the BUSY wait function
somehow fixes the rest of the non-SPI related problems? I may reach out to
Waveshare regarding this.
I added a delay between the transmissions for sending data and I am able to read
all 15000 0xFF's being sent on both the STM and Arduino. So all of the data is
actually going across. Even with the delay in place the data is still scrambled
though.
It looks like if I send the command 0x24 which tells it to start writing to RAM
and then don't send data on the arduino that it ends up writing garbage like the
STM does. But on the STM when I didn't send data nothing happened at all? On the
arduino I can send partial data, lets say 3000/15000 0xFF's and then part of the
screen will clear and the rest will be nonsense. But on the STM it doesn't seem
like I can do this? It almost seems like none of the data is actually going into
RAM.
This means the STM either is not receiving the 0x24, or it is not receiving the
data afterwards. I do not know how to test this though because it looks like
these values are going across the SPI bus. I am at a loss as of now.
DO NOT WRITE TO THE DISPLAY WITHOUT INITING IT FIRST. I THINK I CAUSED A BIT OF
AFTERIMAGE/BURN IN!!
Looking at the scope for the Arduino waveforms there is a delay after command
0x12 before starting the rest of the init sequence, even if BUSY is active? How is
this here? What is doing this? Also there is then a long delay (~30ms) between
initialization and sending the 0x24 command and writing to the RAM. But there is
no delay put in and I am holding BUSY idle. I tried hardcoding these delays into
the STM and its still not working.
I got the AD2 hooked up to the STM and the arduino and have recorded the data
onto the lab computer. There is almost 2/3 less lines in the STM file, this may
mean I am not writing enough data to the display RAM?
Today I want to test possible other reasons why garbage is being displayed on
the display. I hypothesize that another reason could be due to it not properly
being reset. After testing it seems that not doing a reset just makes it so
nothing happens with the display, so that is not a possible problem I don't
believe. I checked on the STM as well and if I don't call the reset function
nothing happens, therefore our reset should be working properly.
I am attempting to verify that the display is getting the command 0x24 which
tells it to start writing to RAM. On the scope I cannot see the sequence
starting with that, but I can see it ending with the sequence to turn on the
display... I cannot seem to confirm this with the scope, I think I may need to
get an AD2 or something of the sort and compare exactly what each is sending,
not based on the code but based on what actually crossed the bus.
So if I write to the RAM with the Arduino all works just fine. But then if I
leave power to the display and move the connection over to the STM and ONLY init
and turn the display on it scrambles the previous image. I do not understand
because I did not put it to sleep, that means the RAM should be kept, so turning
the display on would just render what the Arduino put into RAM. But like I said,
it is still scrambling it. I do not understand what is happening. What is the
point in RAM being saved during light sleep mode if we cannot re render what is
in the RAM?
I should try to write to the RAM on the Arduino and then ONLY turn on the
display without writing to the RAM and see what behavior occurs. Maybe the STM
isn't actually writing to the RAM and is only turning on the display and that
causes issues? I need to test this on the Arduino.
So simply turning on the display without writing to the RAM on the Arduino with
whatever the STM32 wrote to RAM seems to also just keep scrambling it.
And then I just wrote to the RAM with the Arduino to make the screen look nice,
then I ONLY turned on the display and did not write to it with the Arduino, this
also resulted in the same behavior, a scrambled image.
THIS MEANS THAT THE STM32 IS NOT WRITING TO RAM AND IS ONLY TURNING ON THE
DISPLAY. Or it is somehow turning on the display before writing even though I am
not doing that?
I wrote the data structures section of the A3 document.
So I got the display to work, sort of. As of now the screen is just "scrambled",
its just random data on the display when I try to clear it. There were a few
problems that I resolved from before.
First thing is that the BUSY pin is NOT ACTIVE LOW. I do not know how I messed
this up so many times, the datasheet said to WAIT until BUSY goes LOW before
sending other commands, I misinterpreted that. So the whole time the display was
actually ready and I had thought that it was busy. I also was playing around
with some SPI settings but I am unsure if any of them actually did the trick. I
may experiment to find out which one it was.
Second thing was that I was not actually writing to the display. I figured that
it you gave the initialization commands to the display that there would be some
visual feedback but it turns out that there is none unless you actually execute
the command to update/render the RAM onto the display.
I also learned From Shivm that we need to set the coupling mode to normal on the
scope when doing SPI mode for it to properly trigger and save the data.
The first thing I started with was being able to see the SPI signals on the
scope. Yesterday the CS was not even going low unless stepping through with a
debugger meaning a timing issue. I realized that I was not simulating a real
life scenario as I did not have the timing delay in the correct place between
putting the CS low and then high. I switched to using my eink SPI functions and
that fixed that issue, this waits for TXE to go low, which is enough of a delay.
After this I was seeing data on the scope but the data was not correct. Turns
out that scope wants the clock polarity bit to be high, which is not SPI MODE 0
like the display uses. Doing this allowed me to see the correct data, although I
need to figure out how to put the scope into SPI MODE 0 so that we can view the
SPI data as we send it over to the display.
Even with SPI seemingly working the e-ink display's BUSY pin is still constantly
high, right from power on. I looked into the Waveshare code to see how their SPI
is configured and it seems to be the same as mine. Spent a lot of time debugging
and no solution. Next step is to run their Arduino demo code and make sure that
it is not a hardware/wiring issue.
I spent time looking over flowcharts that Zoe made for the A3 document. I also
spent time talking to Mark, Hannah and the Professor about what exactly
constitutes a PSDR. For example the display we purchased comes on a PCB that has
some extra circuitry on it. I would have believed this to be a "module", which
we were told we should not be using. But the Professor said that the e-ink
display PCB would be a whole project on its own so we can use the module. I
don't know that much about hardware, but from what I do know I disagree
(although he did mention possible layout issues and I know nothing about PCB
layout, but the layout on the PCB we have looks fairly simple?). This helps us
out significantly and takes away what could end up being a lot of work.
In man lab we received our e-ink display. This is great news as I am able to
start prototyping and testing out my e-ink driver. Hannah soldered some leads
onto the PCB and I hooked it up to my STM32L4 development board. After testing
my code I found some small errors such as pins being set to the wrong type and
accidentally putting both the RESET pin and the MOSI pin onto the same pin.
Currently the e-ink display is constantly busy, the BUSY pin is always active.
According to the
wiki this is probably because the SPI is
improperly setup or because I have the pins hooked up incorrectly.
I attempted to view the SPI signals on a scope but they are acting weird, also
in a single SPI frame it is not looking like CS is going low even though I am
setting it low and then back to high, I am unsure what is going on with that.
I worked on section 1 of the A3 document. We were told to not reiterate anything
written in that document here, so feel free to check out the document as it is
linked on our website. Part of this included research into battery voltage
monitoring and asking Mark and Hannah as I did not know anything about that.
So based on what I ended up being able to find on the Waveshare website, the
pictures of the product we bought show that it is V2.2, therefore
the initialization code I wrote previously may be wrong
as it was for V1, I also
found the V2 datasheet hidden on
Waveshares website under their wiki
for the
display. So I ended up rewriting the initialization code using
the below flow chart (INSERT PICTURE) and Waveshares example code. Although
there are a few things wrong with the datasheet, for example they say to send
the data in the wrong order for setting the Y-axis RAM address. (INSERT PICTURE
OF MY CODE HERE).
During this time I also started adding functions for drawing pixels and handling
the framebuffer. So since this display is just black and white we are able to
represent each pixel as a single bit instead of a whole byte, this is very
helpful since we are on an embedded device with limited memory, this is 8x more
memory efficient. This means we just need a little bit more logic to handle
this. As with any linear framebuffer, we can find the index into the framebuffer
with `y * WIDTH + x`. As you can imagine we just have to divide this index by 8
and then use `x % 8` to find the offset into the byte.
I started today by setting up the firmware part of out GitHub repo. (insert
picture of repo on browser here). In this repo I initialized a platformIO
project, this is the same tool that we use in 362 for building, running and
debugging our code. This is going to make things significantly easier for us
than writing all of the startup code manually, this saves hassle of debugging
startup code and allows us to focus on more important things.
Next I moved on to writing the first code for the repo. I started with a simple
SPI interface that can be used for any SPI peripheral, this will get us set up
to write the e-ink driver and the SD card driver. This opens up rx and tx
functions and also initalizes the SPI peripheral for the e-ink display. As of
now I am unsure what clock frequency I should run the SPI at, I will experiment
with different ones once we have the hardware. For now I have it set to 2MHz.
I started writing the e-ink driver. This driver has the following extra pins
besides SPI ones:
- BUSY
- D/C
- RESET
So BUSY pin is pulled low when the display is currently doing an operation that
should not be interrupted, this is important so the driver can wait before
executing the next command. The D/C pin allows us to tell the display if the
data that we are sending it over SPI is data or a command. There is also the
option to use 9-bit SPI and specify it that way, this is decided with the BS1
pin (pulled low for 4-wire SPI).
As of now I am unsure if the display we have is V1 or V2. Waveshare does not
allow you to select which one we are purchasing and the only mention of V2 is
saying that it allows a faster full refresh. Also I do not see any mention of V2
in the datasheet.
To set up the initialization of the display I am going through the flow chart
shown below and some example code put out by Waveshare. I will
not list out all of the steps here but the datasheet is linked on our website
and the steps are shown in the screenshot below. I implemented all of this into
the e-ink driver today.
Helped Zoe with debugging SD card. Found 3.3v not on rail to fix one issue,
still have physical drive error
I helped Zoe with debugging the ECE 36200 SD card module. The code was
completeling hanging when trying to mount the SD card. We then started stepping
through the code to try to find the source of the error. We found that it was
failing in the initialization sequence (I would insert a screenshot but I do not
have the code on my computer). Sometimes the 362 dev boards only output ~3V, we
ended up finding out that this was the issue, the sd card reader required 3.3V
and even this small deviation caused the issue. After this a new issue acme up
and this one printed the error string "Physical drive cannot work". We did not
have enough time to continue debugging so the source of this is still unknown.
We need to get this working as all SD card readers use a common protocol, so
even if we are using the SD card reader from 362 for prototyping, we can easily
buy another module and the code *should* still work.
Today I spent time touching up my journals and posting them onto the website.
I also worked on the A2 functional description document. I finished up the part
I was working on which was the computational constraints. Some of the lower
level details of the constraints will need to be ironed out once we know exactly
what metadata will be stored etc. But for now they are sufficient. We should not
need to worry too much as we are choosing an MCU with ample RAM and will be
using an SD card for persistent storage which will have plentiful space.
I also assisted Mark with the section on power. I spent some time writing a
python script which did power calculations to see how long the device will last.
Very rough calculations on my end show ~140 days while Marks were showing ~50
days, although that was with a different MCU standby current. Although our
calculations are not assuming other hardware besides the display and MCU as of
now, also they are not taking into account power losses from regulators or
anything of that sort. These calculations are important as one of the selling
points of our device is long battery life, the goal is to not have to worry
about charging it often so it can just stay in the users backpack to be able to
study whenever.
Today I have been spending time reading through more code that people have
written for e-ink display drivers and their write-ups on it. Since we need to
get prototyping ASAP I have been doing some reading so that we can be prepared
when the display arrives.
this
article has been helpful, especially because it linked to
this Github repo which is going to be a big help as it
is some example firmware for a different MCU directly from Waveshare (the
manufacture of the e-ink display we are buying) themselves! I have yet to look
into this code but will hopefully find time to do that soon.
I also have been looking through the A2 Functional Specification document and
making some slight tweaks before we get ready to submit it. The team has been
doing great work thus far!
In the afternoon I spent some time helping Mark with understanding part of the
hardware schematic for the e-ink display. He found out that the main driver
circuitry needed has to do with boosting voltages. We were confused about
driving the gate of the transistor used in boosting. I looked into this and
found that we do not have to drive the gate of the transistor, this is done by
an internal display controller in the device. This makes things significantly
easier than we had previously thought. We thought that we would have to deal
with waveform (wbf) files and manually controlling the display waveforms.
Luckily displays have these controllers in them that do that for us and we have
to supply the correct voltages in hardware and then do the initialization
sequence and then push the framebuffer in software. We are also hoping to
support partial refresh in the firmware since our display supports that.
I spent some time updating our website. I updated our PSDRs, although our
software one(s) are not final. I also uploaded our microcontroller datasheet and
reference manual.
During our man-lab session I recommended that we use the STM32L4 line of
microcontrollers as it will give us a mix of the processing power we need along
with a low energy usage, especially while in standby. It seems that we are going to go with the STM32L496
as this is the one with the most SRAM which will be helpful for storing numerous
flashcards in memory at once as well as the e-ink framebuffer.
During this session I also recommended that we go with Waveshare e-ink displays
as I have seen and considered using one before for a personal project. They seem
to have decent documentation and are easily purchasable on their website. We
collectively decided on a 4.2 inch display.
First Zoe and I need to have a
board that comes with a hardware driver so we can begin prototyping the firmware
and then later we can buy the raw display to end up in the final product.
I spent more time today looking into the microcontrollers. I was discussing the
differences that I found previously with my teammates and after some discussion
we decided on going with the STM32 over ESP32. During this discussion we talked
about how many open source projects we were looking at were using ESP32 but that
we did not need the wireless connectivity, therefore the extra power draw is not
worth it.
I also spent some time researching the basics of e-ink displays and reading
articles sent by other members of the team as well. We were looking into some
open source hardware drivers for e-ink displays to understand how we can go
about designing our own. I was also looking into software implementations of
e-ink drivers to get some ideas. One example I found is here as well as some more
general e-ink driving documentation such as
here, this includes algorithms for
rendering fonts, drawing lines etc.
I have been looking into ESP32 vs STM32. The power draw alone is a massive
difference that will be a big deal to us since we want a long battery life. In
deep sleep mode with only RTC enabled (I am discussing with RTC enabled so that
we can keep system time in case we need to implement reminders for studying)
the STM32L4 series only uses 0.28µA while ESP32 uses 5µA, that is almost 18x
more current. When running the ESP32 says it uses at a minimum 27mA at 160MHz
which is 0.16mA/MHz while the STM32L4 uses 0.084mA/MHz, so that is about a 2x
difference.
Therefore if the device was used for 2 hours everyday and was in sleep mode for
22 hours the STM32 would use ~13.44 mAh/day at 80MHz while the ESP32 would use
~54.11mAh/day, so the ESP uses ~4x more energy.
Everything else besides this seems to be in favor of the ESP32. They have 4-6x
the SRAM and flash, are faster and have native wireless connectivity if we
decided to go wireless for sending the flashcards to the device
I spent time working on filling out and getting the website setup. I navigated
through the structure to get a good feel of things that we need to update now
and in the future. I updated all of my sections as well as went through and
edited other parts such as adding out GitHub.
I spent the time in class working on getting the website up and running with the
rest of my team. I was having problems on my end with getting the network drive
mounted on my Linux system. Originally I was trying to use sshfs as that is what
I use for mounting my eceprog account to my local system so I can use my local
text editor but this was not working. I found out that I have to use CIFS with
the normal UNIX mount.
During this day I also spent time working on the A1 final project document. I
worked on my personal portion of it, filling out what parts I am working on as
well as writing the description about myself.