Interfering interrupts: I2C inside timing interrupt, and USART

Go To Last Post
31 posts / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I have a project where I'm using I2C inside a timer interrupt to control external hardware, basically using the microcontroller (ATMEGA328pb @ 12MHz) as a feedback PID controller.  This timing interrupt fires every 10ms or so, and consumes about 5ms.  Most of that time is spent dealing with I2C (400kHz, but many bits, waits, etc.)

 

All that works fine on its own.

 

The problem is adding high-speed UART communication; I'd like to use 57600bps, and I'm finding that writing too much, too fast to the UART from an external source causes my microprocessor to lock - and the watchdog isn't even helping (not sure why).  I'm not running out of receive buffer, I think it's that the I2C interrupt is taking too much time.  Typing-speed works fine, but sending scripts and such at full speed locks up the device.

 

I'm using Fleury's bit-banged I2C library.

 

What's the "right" way to do this:  I'd like full-duplex UART and I2C running concurrently, and to preserve my timer ISR that contains all this i2c control stuff.

 

(I can't pull the I2C code out of the ISR, as it's critical.  Is there a reliable I2C library that works inside interrupts, and plays nice with the UART?)

 

Thanks!

 

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

What has priority? That is, what must happen without regard to what else is happening?

 

At 57.6K, one character takes 17.3us. So, you can easily get several characters during the 5ms "consumed" by I2C. So, you will loose characters, guaranteed. 

 

The one choice is to get the I2C stuff out of the ISR and do it (that is, the various bytes and waits) on a polled basis,  triggered by a timer event. That is the only choice that I see. Or, guarantee that you loose characters. 

 

Jim

Jim Wagner Oregon Research Electronics, Consulting Div. Tangent, OR, USA http://www.orelectronics.net

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

jrs454 wrote:
(I can't pull the I2C code out of the ISR, as it's critical.

The short answer is "don't do it in the ISR".

 

Describe "critical".  We already know that the I2C consumes several milliseconds from your post.  [How long actually are the transactions in bytes? I2C speed?  Lessee, at 100kHz I2C clock it takes something over 10 bytes for 1ms, right?  At 400kHz that would be about 50?]

 

As it takes some milliseconds anyway, setting a flag in the timer ISR and handling the transaction in the mainline seems like the obvious approach to me.  YMMV.  In the meantime, USART-driven tx/rx might interrupt a few times for a few microseconds.  On the rx side, when you get to it a few characters would have been gathered.

You can put lipstick on a pig, but it is still a pig.

I've never met a pig I didn't like, as long as you have some salt and pepper.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 1

If you "can't pull the I2C code...", then your timing constraints simply cannot be met.

 

Jim

Jim Wagner Oregon Research Electronics, Consulting Div. Tangent, OR, USA http://www.orelectronics.net

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

jrs454 wrote:
I'm using I2C inside a timer interrupt

Oh dear!

 

ka7ehk wrote:
get the I2C stuff out of the ISR

 

theusch wrote:
don't do it in the ISR

 

I think the consensus has it!

 

Top Tips:

  1. How to properly post source code - see: https://www.avrfreaks.net/comment... - also how to properly include images/pictures
  2. "Garbage" characters on a serial terminal are (almost?) invariably due to wrong baud rate - see: https://learn.sparkfun.com/tutorials/serial-communication
  3. Wrong baud rate is usually due to not running at the speed you thought; check by blinking a LED to see if you get the speed you expected
  4. Difference between a crystal, and a crystal oscillatorhttps://www.avrfreaks.net/comment...
  5. When your question is resolved, mark the solution: https://www.avrfreaks.net/comment...
  6. Beginner's "Getting Started" tips: https://www.avrfreaks.net/comment...
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

jrs454 wrote:
bit-banged I2C
 

 

Could you not use interrupt-driven hardware I2C?

 

Then you could just initiate the transaction in the timer interrupt - but wouldn't have to run the whole thing in the interrupt ... ?

Top Tips:

  1. How to properly post source code - see: https://www.avrfreaks.net/comment... - also how to properly include images/pictures
  2. "Garbage" characters on a serial terminal are (almost?) invariably due to wrong baud rate - see: https://learn.sparkfun.com/tutorials/serial-communication
  3. Wrong baud rate is usually due to not running at the speed you thought; check by blinking a LED to see if you get the speed you expected
  4. Difference between a crystal, and a crystal oscillatorhttps://www.avrfreaks.net/comment...
  5. When your question is resolved, mark the solution: https://www.avrfreaks.net/comment...
  6. Beginner's "Getting Started" tips: https://www.avrfreaks.net/comment...
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Well, if you really want to go the route of nested interrupts, you could.  You'd need to take precautions that the 5ms never overruns the 10ms, along with other considerations including stack space.

 

 

You can put lipstick on a pig, but it is still a pig.

I've never met a pig I didn't like, as long as you have some salt and pepper.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

ka7ehk wrote:

What has priority? That is, what must happen without regard to what else is happening?

 

At 57.6K, one character takes 17.3us. So, you can easily get several characters during the 5ms "consumed" by I2C. So, you will loose characters, guaranteed. 

 

The one choice is to get the I2C stuff out of the ISR and do it (that is, the various bytes and waits) on a polled basis,  triggered by a timer event. That is the only choice that I see. Or, guarantee that you loose characters. 

 

Jim

 

Thanks.  Control loop is all-important. 

 

(Pretty sure one character is 173us, not 17.3us, but point is taken)

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Go on. I2C @ 100kHz is 90us per byte. USART @ 57600 baud is 174us.
I bet that your Timer interrupts occur less frequently.
.
If Timet, I2C, USART all use interrupts @ 100 cycles per ISR(), you should be able to service everything with F_CPU @ >2MHz.
.
In practice, you could probably poll one device and use interrupts for the other two. But hey-ho, you might just as well do everything with interrupts.
.
Clearly, you have to write efficient ISRs in less than 100 cycles. Even at 200 cycles a 4MHz chip is ok. 20MHz is severe overkill.
.
David.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

awneil wrote:

jrs454 wrote:
bit-banged I2C
 

 

Could you not use interrupt-driven hardware I2C?

 

Then you could just initiate the transaction in the timer interrupt - but wouldn't have to run the whole thing in the interrupt ... ?

 

Thanks, I think this is what I want: the timer interrupt to trigger, starting i2c communication, interrupt ends, but the i2c subsequently happens concurrently with the UART interrupt and handling.  

 

I haven't seen any implementations of this, though - the i2c code I've seen (hardware) doesn't seem to work inside interrupts (or it impedes other interrupts), and I'm not sure how to otherwise use an interrupt to execute the i2c sequence that happens outside the ISR.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

awneil wrote:

jrs454 wrote:
I'm using I2C inside a timer interrupt

Oh dear!

 

ka7ehk wrote:
get the I2C stuff out of the ISR

 

theusch wrote:
don't do it in the ISR

 

I think the consensus has it!

 

 

I'd love to.  The trouble is, the main loop does other various things, which may interfere with the control loop timing.  That control loop has to run reliably and regularly.  I'm not sure it's possible to remove the control commands (i2c) from the ISR.

 

I want those i2c commands to execute reliably and on time.  I don't think there's any other way to do that than via an interrupt?

Last Edited: Mon. Jun 5, 2017 - 07:08 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

jrs454 wrote:
bit-banged I2C

I missed that.

 

I just got accused last week of being the naysayer, and I wasn't in full Surly Curmudgeon mode.

 

But still, you've got a tight app and you are bit-banging a slow protocol?  Why not extend that and bit-bang your UART link?  Ridiculous, you say?  Tell me why the analogy is different.

 

jrs454 wrote:
That control loop has to run reliably and regularly.

You have to lay out the requirements for your app.  That includes the tolerances on that control loop.

 

If the [unspecified] I2C device is the big bottleneck, then perhaps consider an alternative.

 

You can have a lot of stuff going on in an AVR app and still get it all done.  Yeah, when a UART link or two are passing messages there are a lot of interrupts firing.  While I worked on CNC in the past with 2ms control loops, most AVR apps are a few milliseconds (say, 8-10ms) and a bit of variance doesn't matter much.

 

Obviously if you are going to sit in an ISR for 5ms or more then something will have to give.

You can put lipstick on a pig, but it is still a pig.

I've never met a pig I didn't like, as long as you have some salt and pepper.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Ha, I'm using bit-banged I2C since that's what's been working fine in the system for years.  Now we're adding a UART interface, and the trouble begins.  Changing the hardware isn't a practical option, unfortunately.

 

But is it not practical to do what you hinted at above, and what I more explicitly considered?  Trigger the i2c with an interrupt, but let the i2c subsequently run "freely" concurrently with UART (and main code) subsequently?

 

If it comes to it I think we can tolerate a little bit of timing error on the control loop.  I just remeasured it, it's consuming about 3.5ms and triggering every 7ms.  But during i2c messaging, interrupts are disabled, causing obvious UART problems.  I suppose another option is to split up the i2c messages somehow (allowing a uart character to be stored between executions).  None is very appealing though.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

jrs454 wrote:
If it comes to it I think we can tolerate a little bit of timing error on the control loop. I just remeasured it, it's consuming about 3.5ms and triggering every 7ms.

And above you said you were sitting in the 10ms ISR doing I2C work for 5ms.  So obviously there is some jitter (and a fairly high percentage).

 

If you let the UART gather your characters and then process the message later, it should be able to be interleaved.  If you really want to make an elaborate I2C sequence and not leave the ISR, then at your "pause" times you could poll the UART.

You can put lipstick on a pig, but it is still a pig.

I've never met a pig I didn't like, as long as you have some salt and pepper.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Most I2C code that uses the AVR TWI will wait in a loop for the TWI operation to complete.   This wait loop consists of checking the TWINT bit in the TWCR register.  When this bit is set, then the TWI operation is complete.  For a write operation, the write data in the TWDR register has been sent.  For a read op, the data from the I2C slave is ready to be read.   The TWINT bit is cleared by commencing the next operation by writing to TWCR with the TWINT set.

 

The TWI interrupt when enabled will not wait in a polling loop.  When the TWI operation is finished, it will execute the TWI interrupt. This IRQ code will check the TWI status byte returned from the completed operation,  branch to the code that commences the next operation of the TWI sequence, and then exit.  This takes very little time: much less than the time spent waiting in a TWINT polling loop.

 

The key to getting the Timer IRQs,  the TWI IRQs, and the USART IRQs all working together in the background is to make each IRQ, regardless of type, be very short.  Each IRQ should set global flags for the main code, or execute a predefined option according to the current state of the peripheral that generated the IRQ.  The TWI is the most complex of the IRQs, but even so it only has about four to six options that are checked.

 

If the TWI library doesn't implement the TWI IRQ, then keep checking on the web for one that does.  Many people have written code to address this very issue.  Check also for code that is written in assembler even if you're not flexible in its use.  The TWI IRQ isn't too complex even in ASM; it consists of reading the status code and branching accordingly.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Currently the UART interrupt does gather characters, and the main loop processes them.  This is reliable for typed speeds, but not faster (above a dozen characters or so).  Going too fast misses characters, or even freezes the chip.  (Oddly, without triggering the watchdog reset).

 

I suppose I could switch to polling the UART.  Even *gasp* inside the i2c's isr!

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Simonetta wrote:
If the TWI library doesn't implement the TWI IRQ, then keep checking on the web for one that does
 

http://www.lmgtfy.com?q=avr+i2c+...

Top Tips:

  1. How to properly post source code - see: https://www.avrfreaks.net/comment... - also how to properly include images/pictures
  2. "Garbage" characters on a serial terminal are (almost?) invariably due to wrong baud rate - see: https://learn.sparkfun.com/tutorials/serial-communication
  3. Wrong baud rate is usually due to not running at the speed you thought; check by blinking a LED to see if you get the speed you expected
  4. Difference between a crystal, and a crystal oscillatorhttps://www.avrfreaks.net/comment...
  5. When your question is resolved, mark the solution: https://www.avrfreaks.net/comment...
  6. Beginner's "Getting Started" tips: https://www.avrfreaks.net/comment...
Last Edited: Mon. Jun 5, 2017 - 07:48 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Simonetta wrote:

Most I2C code that uses the AVR TWI will wait in a loop for the TWI operation to complete.   This wait loop consists of checking the TWINT bit in the TWCR register.  When this bit is set, then the TWI operation is complete.  For a write operation, the write data in the TWDR register has been sent.  For a read op, the data from the I2C slave is ready to be read.   The TWINT bit is cleared by commencing the next operation by writing to TWCR with the TWINT set.

 

The TWI interrupt when enabled will not wait in a polling loop.  When the TWI operation is finished, it will execute the TWI interrupt. This IRQ code will check the TWI status byte returned from the completed operation,  branch to the code that commences the next operation of the TWI sequence, and then exit.  This takes very little time: much less than the time spent waiting in a TWINT polling loop.

 

The key to getting the Timer IRQs,  the TWI IRQs, and the USART IRQs all working together in the background is to make each IRQ, regardless of type, be very short.  Each IRQ should set global flags for the main code, or execute a predefined option according to the current state of the peripheral that generated the IRQ.  The TWI is the most complex of the IRQs, but even so it only has about four to six options that are checked.

 

If the TWI library doesn't implement the TWI IRQ, then keep checking on the web for one that does.  Many people have written code to address this very issue.  Check also for code that is written in assembler even if you're not flexible in its use.  The TWI IRQ isn't too complex even in ASM; it consists of reading the status code and branching accordingly.

 

Thanks.  This might be the right solution.  I will have to research it though.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

jrs454 wrote:
Currently the UART interrupt does gather characters, and the main loop processes them.  This is reliable for typed speeds, but not faster (above a dozen characters or so).

Something seriously wrong there, then!!

 

surprise

 

A UART interrupt feeding a so-called Ring Buffer is the standard way to do it!!

Top Tips:

  1. How to properly post source code - see: https://www.avrfreaks.net/comment... - also how to properly include images/pictures
  2. "Garbage" characters on a serial terminal are (almost?) invariably due to wrong baud rate - see: https://learn.sparkfun.com/tutorials/serial-communication
  3. Wrong baud rate is usually due to not running at the speed you thought; check by blinking a LED to see if you get the speed you expected
  4. Difference between a crystal, and a crystal oscillatorhttps://www.avrfreaks.net/comment...
  5. When your question is resolved, mark the solution: https://www.avrfreaks.net/comment...
  6. Beginner's "Getting Started" tips: https://www.avrfreaks.net/comment...
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

awneil wrote:

jrs454 wrote:

Currently the UART interrupt does gather characters, and the main loop processes them.  This is reliable for typed speeds, but not faster (above a dozen characters or so).

 

Something seriously wrong there, then!!

 

surprise

 

A UART interrupt feeding a so-called Ring Buffer is the standard way to do it!!

 

Well, yeah, it's that other pesky interrupt with the I2C that makes this a hard problem.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

jrs454 wrote:
Well, yeah, it's that other pesky interrupt with the I2C that makes this a hard problem.

We keep beating this here.  Take one of the paths already mentioned.  You can nest interrupts.  Or not, and move the I2C elsewhere.  Or poll the UART during your I2C.  Or otherwise reconstruct.

 

If you have a revised plan and have problems or particular questions, I'll take a peek.  Otherwise, I'm out.

You can put lipstick on a pig, but it is still a pig.

I've never met a pig I didn't like, as long as you have some salt and pepper.

Last Edited: Mon. Jun 5, 2017 - 08:50 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

It all seems very straightforward.   There are plenty of interrupt driven USART examples.   And plenty of interrupt driven I2C.

 

You just need to sit down with a nice cup of tea.   And design your project.

Then ask for help with any area that you are unsure of.

 

I am assuming of course that the Serial is using the hardware USART pins.   And the I2C is using the hardware TWI pins.

 

Be honest.  If you are "bit-banging" I2C on GPIO pins,  you can't use the TWI peripheral or TWI interrupt.   Since I2C has no special timing,   you can bang it in the foreground with Timer and USART working with interrupts.     You probably need to run the AVR at  a faster and baud friendly F_CPU.

 

David.

Last Edited: Mon. Jun 5, 2017 - 09:12 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

it would really help to know what AVR model we are talking about here!

Does it have a TWI?  USART and what is the xtal frequency?

 

Jim

 

 

 

 

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Thanks.  Project is already designed and fully functional.  This is to add a UART interface without (hopefully) significant changes.  That's why it's a little more challenging than starting fresh.

 

We are currently bit banging the I2C, but we need not do so in the future, necessarily, as the correct TWI pins are indeed used for I2C (and the hardware USART works fine).  Per the above, I was hoping to have the I2C "trigger" via the interrupt, but then continue executing outside of the ISR, while the other interrupts (uart) are working normally.  I'm not sure how to do that, though, and I have to dig around for another library or examples.

 

Having a timer handle individual bits might work, since 1/400kHz=2.5us might be workable, though at 12MHz that's only 30 instructions.  Such an ISR can probably be done in a dozen or so instructions.  (Pin assignment, and store in a shift register?)

 

Getting proper hardware TWI working seems "right", but most difficult, and is the least familiar to me (with potentially nested interrupts).  I suppose that's the area I'm unsure of.

 

 

 

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

ki0bk wrote:

it would really help to know what AVR model we are talking about here!

Does it have a TWI?  USART and what is the xtal frequency?

 

Jim

 

 

This was in my original post.  ATMEGA328pb @ 12MHz.  TWI for I2C is an option, and is already implemented for the UART.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I would avoid nested interrupts. AVR315 app note shows interrupt driven Master but I would make improvements.
.
You do realise that the Arduino uses interrupts for Serial and for Wire. However you would need to rearrange the logic for efficiency. i.e. you start an I2C transaction but you do not need to sit waiting for completion. You get on with the rest of your life. You only need to check that the previous transaction has finished when you want to start a new one.
.
I suggest that you prototype your ideas on an Arduino. Adapt as necessary.
.
David.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 2

Sorry to sound like a broken record but this just highlights once again that "organically grown" rather than formally designed software does not work. If you build a house of straw on a swamp then you should expect it to collapse wen you try to add the concrete spire!

 

At first the idea of ditching all your current software (which "works") and starting over with a clean sheet sounds like an insane idea. Surely it is going to take days/weeks. However it has two advantages. You already know from what you have done how to achieve most of the things and having done it once and encountered the pit-falls you know what to avoid.

 

Clearly bit-bang I2C is one obvious thing to avoid next time round.

 

We haven't seen your code but for the UART thing what you will want is an RXC that does little more than feed a ring buffer. Process the bytes out of the buffer at leisure when there is "spare time" - don't be tempted to put any of the actual data processing into the ISR. Same goes for the I2C (if you choose to do it by interrupt not polling). Just do the basic send/receive in the ISR - worry about any higher level processing in the leisure of the main splin loop.

 

In redesigning you will have the advantage of the "prototype" you already created to draw up memory, time and power budgets. You now know which parts are time critical and which are not. Design accordingly.

 

Oh and don't start coding until you have a clear and complete design in your mind (or better still on paper).

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

clawson wrote:

Sorry to sound like a broken record but this just highlights once again that "organically grown" rather than formally designed software does not work. If you build a house of straw on a swamp then you should expect it to collapse wen you try to add the concrete spire!

 

At first the idea of ditching all your current software (which "works") and starting over with a clean sheet sounds like an insane idea. Surely it is going to take days/weeks. However it has two advantages. You already know from what you have done how to achieve most of the things and having done it once and encountered the pit-falls you know what to avoid.

 

Clearly bit-bang I2C is one obvious thing to avoid next time round.

 

We haven't seen your code but for the UART thing what you will want is an RXC that does little more than feed a ring buffer. Process the bytes out of the buffer at leisure when there is "spare time" - don't be tempted to put any of the actual data processing into the ISR. Same goes for the I2C (if you choose to do it by interrupt not polling). Just do the basic send/receive in the ISR - worry about any higher level processing in the leisure of the main splin loop.

 

In redesigning you will have the advantage of the "prototype" you already created to draw up memory, time and power budgets. You now know which parts are time critical and which are not. Design accordingly.

 

Oh and don't start coding until you have a clear and complete design in your mind (or better still on paper).

 

I'm not sure I quite agree; this isn't really "organically grown" - we're adding a specific, fairly modest feature to something that's worked perfectly for ages.  

 

The UART interrupt does in fact feed a buffer and little else.

 

But again the hard part is that the critical control loop needs to be executed reliably, and the monitoring/control is executed via I2C.  If it's removed from an interrupt and placed in the main loop, it may not run reliably, as other more mundane processes may bog it down.  While it's certainly best practice to have an interrupt contain as little code as possible, sometimes, when critical code must be run, an interrupt still seems appropriate.

 

That's what makes this an unusual case, and an interesting problem.

 

Switching from a bit-bang I2C to the TWI hardware is a perfectly reasonable solution - I'm just asking for guidance with that given these unusual constraints, such as:

 

1.  A TWI solution that allows the UART interrupt to still work reliably during communication.

2.  Ensuring that the I2C control/read is running regularly and reliably, such as via an interrupt

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Go on.   You can calculate the worst case.   e.g I2C @ 90us, USART @ 174us, ...

A 100 cycle ISR() takes 7us to complete @ 16MHz.

 

You have only got to sit down with a nice cup of tea.   Even have a biscuit.   It is pretty simple to calculate.

 

In practice,   you might worry about a worst case in an embedded app.    You happily live with your PC running multiple apps.   The work gets done in the end.

 

How large are the I2C transactions?   How large are the USART transactions?

 

Put some numbers in.   e.g. Timer event occurs every 5ms,    10 bytes are sent on I2C,   20 char message on USART,  ...

 

If you make good use of the hardware peripherals,   your humble AVR is effectively "multitasking".    Much like your PC.

 

David.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

jrs454 wrote:
I'm using Fleury's bit-banged I2C library.

The h/w based TWI code that is part of the Fleury library has the same API as the bit bang version, so switching to TWI should be painless if your using the same pin's. 

 

What is driving the need for serial interface, control or reporting, if you can, a slow baud rate(<9600...1200) may be easier to start with and have less effect on current operation.

 

Jim

 

 

 

 

 

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

does someone got this issue solved?

I got the same problem now, I use AVR315 code, it works fine when USART in polling mode,

as long as switch USART from

 UCSR0B = (1 << RXEN0) | (0 << RXCIE0) | (1 << TXEN0);

to

 UCSR0B = (1 << RXEN0) | (1 << RXCIE0) | (1 << TXEN0);

it stucked in TWI routine, and never out of it.

 

Last Edited: Wed. Feb 6, 2019 - 09:00 PM