Thanks, PaulS: it works well now 
For anyone else who has the same (unlikely niche) problem, here is my code:
#include <TimedAction.h>
#include <LiquidCrystal.h>
#include <Keypad.h>
#include <String.h>
const byte ROWS = 4;
const byte COLS = 4;
const String fversion = "v0.1";
const unsigned long bwLimit = 50000000;
const int lcdTimer = 1;
enum Screen {s_root, s_staticFreq, s_staticFreqOutput, s_sweepFreqStart, s_sweepFreqEnd, s_sweepStep};
char keys[ROWS][COLS] = {
{'*', '0', '#', 'D'},
{'7', '8', '9', 'C'},
{'4', '5', '6', 'B'},
{'1', '2', '3', 'A'} };
byte rowPins[ROWS] = {44, 45, 46, 47};
byte colPins[COLS] = {43, 42, 41, 40};
unsigned long staticFreq;
unsigned long sweepFreqStart;
unsigned long sweepFreqEnd;
unsigned long sweepStep;
Screen currentScreen = s_root;
int lcdScrollLength = 0;
String lcdStaticTopLine;
int lcdScrollDelay = 1000; // Delay while screen has reached either end of the scroll cycle
int lcdScrollLetterDelay = 150; // Delay between each letter
int lcdScrollStage = 0;
int lcdPositionCounter = 0;
boolean lcdPositionCounterSet = false;
int keyHoldDelay = 200; // When a key is held, numbers are input every keyHoldDelay milliseconds.
int initialKeyHoldDelay = 1500; // Time you must hold a key before it will print several characters
TimedAction lcdScrollTimer = TimedAction(lcdScrollDelay, lcdScrollEvent);
TimedAction keyHoldTimer = TimedAction(initialKeyHoldDelay, keyHold);
LiquidCrystal lcd(22, 23, 24, 25, 26, 27);
Keypad kpd = Keypad(makeKeymap(keys), rowPins, colPins, ROWS, COLS);
char currentKey;
void setup() {
lcdScrollTimer.disable();
keyHoldTimer.disable();
kpd.addEventListener(updateScreen);
kpd.setHoldTime(400);
// Serial.begin(9600);
// Debug only
lcd.begin(16, 2);
lcd.clear();
lcd.setCursor(0,0);
lcd.print("AD9850 DDS");
lcd.setCursor(0,1);
lcd.print("Firmware ");
lcd.print(fversion);
delay(2000);
lcd.clear();
lcd.setCursor(0,0);
lcd.print("A: Static freq");
lcd.setCursor(0,1);
lcd.print("B: Sweep freqs");
}
void loop() {
kpd.getKey();
lcdScrollTimer.check();
keyHoldTimer.check();
}
void lcdScrollEvent() {
Serial.println("lcdScrollEvent called");
switch (lcdScrollStage) {
case 0: {
if (!lcdPositionCounterSet) {
lcdPositionCounter = 0;
lcdPositionCounterSet = true;
}
if (lcdPositionCounter >= lcdScrollLength) {
lcdScrollStage += 1;
lcdScrollTimer.setInterval(lcdScrollDelay);
break;
}
lcd.scrollDisplayLeft();
lcd.setCursor(lcdPositionCounter + 1, 0);
lcd.print(lcdStaticTopLine);
lcdPositionCounter++;
lcdScrollTimer.setInterval(lcdScrollLetterDelay); // TEH MAGIC!
break;
}
case 1: {
for (lcdPositionCounter = 0; lcdPositionCounter < lcdScrollLength; lcdPositionCounter++) {
lcd.scrollDisplayRight();
}
lcdScrollStage -= 1;
// Lcd scroll timer already set: no need to do so again here
lcdPositionCounterSet = false;
lcd.setCursor(0,0);
lcd.print(lcdStaticTopLine);
break;
}
}
}
void lcdScroll(int length, String topLine) {
lcdScrollTimer.setInterval(lcdScrollDelay); // Reset interval
lcdScrollTimer.reset(); // Reset timer
lcdPositionCounter = 0; // Reset position
lcdScrollLength = length;
lcdStaticTopLine = topLine;
lcdScrollTimer.enable();
}
void noLcdScroll() {
lcdScrollTimer.disable();
}
unsigned long screenInput(unsigned long var, KeypadEvent key) {
int tkey = key - '0';
var = var * 10;
var = var + tkey;
if (var > bwLimit) var = bwLimit; // Restrict bandwidth to pre-set limit
lcd.setCursor(1,1);
lcd.print(var);
keyHoldTimer.reset();
keyHoldTimer.enable(); // Start key hold timer
return var;
}
unsigned long screenBackspace(unsigned long var) {
var -= var % 10; // Remove last digit to prevent any division errors
var /= 10; // Divide by ten
lcd.setCursor(1,1);
lcd.print(" "); // 15 spaces - clear bottom line (except for '>')
lcd.setCursor(1,1);
if (var != 0) {lcd.print(var);}
keyHoldTimer.reset();
keyHoldTimer.enable();
return var;
}
void keyHold() {
keyHoldTimer.setInterval(keyHoldDelay);
unsigned long var;
if (kpd.getState() == HOLD) {
switch (currentKey) {
case '*': {
switch (currentScreen) {
case s_staticFreq: {
staticFreq = screenBackspace(staticFreq);
break;
}
case s_sweepFreqStart: {
sweepFreqStart = screenBackspace(sweepFreqStart);
break;
}
case s_sweepFreqEnd: {
sweepFreqEnd = screenBackspace(sweepFreqEnd);
break;
}
case s_sweepStep: {
sweepStep = screenBackspace(sweepStep);
break;
}
}
break;
}
case '#':
case 'A':
case 'B':
case 'C':
case 'D': {
keyHoldTimer.disable(); // Cancel timer for non-numerical keys (apart from * which is backspace) otherwise it would continue indefinitely
break;
}
default: { // Number keys
switch (currentScreen) {
case s_staticFreq: {
staticFreq = screenInput(staticFreq, currentKey);
break;
}
case s_sweepFreqStart: {
sweepFreqStart = screenInput(sweepFreqStart, currentKey);
break;
}
case s_sweepFreqEnd: {
sweepFreqEnd = screenInput(sweepFreqEnd, currentKey);
break;
}
case s_sweepStep: {
sweepStep = screenInput(sweepStep, currentKey);
break;
}
}
break;
}
}
}
else {
keyHoldTimer.disable();
keyHoldTimer.setInterval(initialKeyHoldDelay);
}
}
void updateScreen(KeypadEvent key) {
currentKey = key;
if (kpd.getState() == PRESSED) {
if (key == 'C') {
noLcdScroll();
lcd.clear();
lcd.noBlink();
lcd.setCursor(0,0);
lcd.print("A: Static freq");
lcd.setCursor(0,1);
lcd.print("B: Sweep freqs");
staticFreq = 0;
sweepFreqStart = 0;
sweepFreqEnd = 0;
sweepStep = 0;
currentScreen = s_root;
}
switch (currentScreen) {
case s_root: {
switch (key) {
case 'A': {
lcd.clear();
lcd.setCursor(0,0);
lcd.print("Enter frequency");
lcd.setCursor(0,1);
lcd.print(">");
lcd.blink();
currentScreen = s_staticFreq;
break;
}
case 'B': {
lcd.clear();
lcd.setCursor(0,0);
lcd.print("Enter start freq");
lcd.setCursor(0,1);
lcd.print(">");
lcd.blink();
currentScreen = s_sweepFreqStart;
break;
}
}
break;
}
case s_staticFreq: {
switch (key) {
case '*': {
staticFreq = screenBackspace(staticFreq);
break;
}
case '#': {
if (staticFreq > 0) {
lcd.noBlink();
lcd.clear();
lcd.setCursor(0,0);
lcd.print("Beginning output");
lcd.setCursor(0,1);
lcd.print("Frequency(Hz): ");
lcd.print(staticFreq);
lcd.home();
String scrollLen = String(staticFreq);
lcdScroll(scrollLen.length() - 1, "Beginning output");
currentScreen = s_staticFreqOutput;
// Set AD9850 module here
}
break;
}
case 'A':
case 'B':
case 'C':
case 'D': {break;}
default: {
staticFreq = screenInput(staticFreq, key);
break;
}
}
break;
}
case s_sweepFreqStart: {
switch (key) {
case '*': {
sweepFreqStart = screenBackspace(sweepFreqStart);
break;
}
case '#': {
if (sweepFreqStart > 0) {
lcd.clear();
lcd.setCursor(0,0);
lcd.print("Enter end freq");
lcd.setCursor(0,1);
lcd.print(">");
lcd.blink();
currentScreen = s_sweepFreqEnd;
}
break;
}
case 'A':
case 'B':
case 'C':
case 'D': {break;}
default: {
sweepFreqStart = screenInput(sweepFreqStart, key);
break;
}
}
break;
}
}
}
}